@brandsync/wc 1.20.0 → 1.20.1

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 (97) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/REFERENCE.md +2 -2
  3. package/custom-elements.json +2 -2
  4. package/dist/brandsync-wc/brandsync-wc.esm.js +1 -1
  5. package/dist/brandsync-wc/p-14719554.entry.js +1 -0
  6. package/dist/brandsync-wc/p-39c50762.entry.js +1 -0
  7. package/dist/brandsync-wc/p-60636c6c.entry.js +1 -0
  8. package/dist/brandsync-wc/p-6ae75ed0.entry.js +1 -0
  9. package/dist/brandsync-wc/p-6b1ba5ec.entry.js +1 -0
  10. package/dist/brandsync-wc/p-aa99b1f8.entry.js +1 -0
  11. package/dist/brandsync-wc/p-b6dc9234.entry.js +1 -0
  12. package/dist/brandsync-wc/p-c0e5db3d.entry.js +1 -0
  13. package/dist/brandsync-wc/{p-16f15ba5.entry.js → p-dc59a735.entry.js} +1 -1
  14. package/dist/cjs/bs-accordion.cjs.entry.js +1 -1
  15. package/dist/cjs/bs-carousel.cjs.entry.js +1 -1
  16. package/dist/cjs/bs-chip-informative.cjs.entry.js +1 -1
  17. package/dist/cjs/bs-company-switcher.cjs.entry.js +2 -2
  18. package/dist/cjs/bs-popover.cjs.entry.js +25 -1
  19. package/dist/cjs/bs-search-item.cjs.entry.js +1 -1
  20. package/dist/cjs/bs-shortcut-row.cjs.entry.js +1 -1
  21. package/dist/cjs/bs-slider.cjs.entry.js +1 -1
  22. package/dist/cjs/bs-stepper-step.cjs.entry.js +1 -1
  23. package/dist/collection/components/bs-accordion/bs-accordion.js +1 -1
  24. package/dist/collection/components/bs-carousel/bs-carousel.js +2 -2
  25. package/dist/collection/components/bs-carousel/bs-carousel.stories.js +9 -5
  26. package/dist/collection/components/bs-chip/bs-chip-informative/bs-chip-informative.js +1 -1
  27. package/dist/collection/components/bs-company-switcher/bs-company-switcher.cmp.test.js +13 -7
  28. package/dist/collection/components/bs-company-switcher/bs-company-switcher.js +2 -2
  29. package/dist/collection/components/bs-composer/bs-composer.stories.js +1 -1
  30. package/dist/collection/components/bs-dropdown/bs-dropdown.cmp.test.js +1 -3
  31. package/dist/collection/components/bs-menu-item/bs-menu-item.stories.js +19 -15
  32. package/dist/collection/components/bs-popover/bs-popover.cmp.test.js +11 -4
  33. package/dist/collection/components/bs-popover/bs-popover.js +26 -2
  34. package/dist/collection/components/bs-search-item/bs-search-item.js +1 -1
  35. package/dist/collection/components/bs-shortcut-row/bs-shortcut-row.js +1 -1
  36. package/dist/collection/components/bs-slider/bs-slider.js +1 -1
  37. package/dist/collection/components/bs-stepper/bs-stepper-step/bs-stepper-step.js +1 -1
  38. package/dist/collection/components/bs-tab/bs-tab/bs-tab.stories.js +7 -7
  39. package/dist/collection/components/bs-toolbar/bs-toolbar.cmp.test.js +1 -6
  40. package/dist/collection/components/bs-tree-item/bs-tree-item.stories.js +6 -4
  41. package/dist/collection/components/disabled-aria.cmp.test.js +19 -0
  42. package/dist/components/bs-accordion.js +1 -1
  43. package/dist/components/bs-app-switcher.js +1 -1
  44. package/dist/components/bs-carousel.js +1 -1
  45. package/dist/components/bs-chip-informative.js +1 -1
  46. package/dist/components/bs-command-palette.js +1 -1
  47. package/dist/components/bs-company-switcher.js +1 -1
  48. package/dist/components/bs-cookie-consent.js +1 -1
  49. package/dist/components/bs-datepicker.js +1 -1
  50. package/dist/components/bs-dropdown.js +1 -1
  51. package/dist/components/bs-global-search.js +1 -1
  52. package/dist/components/bs-input.js +1 -1
  53. package/dist/components/bs-keyboard-shortcuts.js +1 -1
  54. package/dist/components/bs-mega-menu.js +1 -1
  55. package/dist/components/bs-menu-block.js +1 -1
  56. package/dist/components/bs-popover.js +1 -1
  57. package/dist/components/bs-qr-code.js +1 -1
  58. package/dist/components/bs-search-item.js +1 -1
  59. package/dist/components/bs-session-timeout.js +1 -1
  60. package/dist/components/bs-shortcut-row.js +1 -1
  61. package/dist/components/bs-slider.js +1 -1
  62. package/dist/components/bs-stepper-step.js +1 -1
  63. package/dist/components/bs-timepicker.js +1 -1
  64. package/dist/components/bs-toolbar.js +1 -1
  65. package/dist/components/bs-whats-new-entry.js +1 -1
  66. package/dist/components/bs-whats-new.js +1 -1
  67. package/dist/components/{p-D3XmFqCQ.js → p-B5yjDh8h.js} +1 -1
  68. package/dist/components/p-B81aicKA.js +1 -0
  69. package/dist/components/{p-DYpJMHAH.js → p-C3YETziz.js} +1 -1
  70. package/dist/components/{p-DztU3DwY.js → p-CKTbatgt.js} +1 -1
  71. package/dist/components/p-DUV80kt-.js +1 -0
  72. package/dist/components/p-_zxYDrzi.js +1 -0
  73. package/dist/esm/bs-accordion.entry.js +1 -1
  74. package/dist/esm/bs-carousel.entry.js +1 -1
  75. package/dist/esm/bs-chip-informative.entry.js +1 -1
  76. package/dist/esm/bs-company-switcher.entry.js +2 -2
  77. package/dist/esm/bs-popover.entry.js +25 -1
  78. package/dist/esm/bs-search-item.entry.js +1 -1
  79. package/dist/esm/bs-shortcut-row.entry.js +1 -1
  80. package/dist/esm/bs-slider.entry.js +1 -1
  81. package/dist/esm/bs-stepper-step.entry.js +1 -1
  82. package/dist/types/components/bs-carousel/bs-carousel.d.ts +1 -1
  83. package/dist/types/components/bs-popover/bs-popover.d.ts +9 -1
  84. package/dist/types/components/disabled-aria.cmp.test.d.ts +1 -0
  85. package/package.json +6 -1
  86. package/reference.json +2 -2
  87. package/dist/brandsync-wc/p-050270f5.entry.js +0 -1
  88. package/dist/brandsync-wc/p-0f864a4f.entry.js +0 -1
  89. package/dist/brandsync-wc/p-105c316e.entry.js +0 -1
  90. package/dist/brandsync-wc/p-7632f773.entry.js +0 -1
  91. package/dist/brandsync-wc/p-86fdbe8f.entry.js +0 -1
  92. package/dist/brandsync-wc/p-c9888d09.entry.js +0 -1
  93. package/dist/brandsync-wc/p-e6f57fae.entry.js +0 -1
  94. package/dist/brandsync-wc/p-ff14edb6.entry.js +0 -1
  95. package/dist/components/p-CXv8kx8w.js +0 -1
  96. package/dist/components/p-CePOtLtZ.js +0 -1
  97. package/dist/components/p-DBgafacj.js +0 -1
@@ -86,6 +86,7 @@ const BsPopover = class {
86
86
  });
87
87
  }
88
88
  componentDidRender() {
89
+ this.syncTriggerAria();
89
90
  if (!this.open && this.closing && this.exitPending) {
90
91
  this.exitPending = false;
91
92
  this.playExit();
@@ -161,6 +162,29 @@ const BsPopover = class {
161
162
  this.open = next;
162
163
  this.bsOpenChange.emit(next);
163
164
  }
165
+ /**
166
+ * Keeps `aria-haspopup`/`aria-expanded` on the slotted trigger -- the element that is actually
167
+ * focusable and announced -- rather than on the role-less wrapper div, where they are invalid ARIA.
168
+ * `bs-*` triggers (bs-button, bs-icon-button) forward these to their inner `<button>` through their
169
+ * JS properties; an attribute on the host would sit on a role-less element. A trigger that already
170
+ * declares its own `aria-haspopup` (e.g. bs-dropdown's `menu`) keeps it.
171
+ */
172
+ syncTriggerAria = () => {
173
+ const trigger = this.triggerSlot?.assignedElements()[0];
174
+ if (!trigger)
175
+ return;
176
+ if (trigger.tagName.startsWith('BS-')) {
177
+ const t = trigger;
178
+ if (!t.ariaHasPopup)
179
+ t.ariaHasPopup = 'true';
180
+ t.ariaExpanded = String(this.open);
181
+ }
182
+ else {
183
+ if (!trigger.hasAttribute('aria-haspopup'))
184
+ trigger.setAttribute('aria-haspopup', 'true');
185
+ trigger.setAttribute('aria-expanded', String(this.open));
186
+ }
187
+ };
164
188
  focusTrigger() {
165
189
  const first = this.triggerSlot?.assignedElements()[0];
166
190
  first?.focus();
@@ -183,7 +207,7 @@ const BsPopover = class {
183
207
  }
184
208
  };
185
209
  render() {
186
- return (index.h("div", { key: '60037b8ebeead636ca7cfb1d1fb5656662053854', class: "bs-popover" }, index.h("div", { key: '216a52057acbae40f00d5c7e09ad7141fe356a3b', part: "trigger", class: "bs-popover__trigger", "aria-haspopup": "true", "aria-expanded": String(this.open), "aria-controls": this.popupId, onClick: this.onTriggerClick, onKeyDown: this.onTriggerKeyDown }, index.h("slot", { key: '08bd0b15dbf67de5fbfa5323570886f2ea7a5468', name: "trigger", ref: el => (this.triggerSlot = el) })), index.h("div", { key: '5e409b359b7afcf4da286cfb66a264897698018d', part: "popup", id: this.popupId, class: "bs-popover__popup", tabindex: "-1", hidden: !this.open && !this.closing, ref: el => (this.popupEl = el) }, index.h("slot", { key: '35a194b6369502fa0da50795aff5ee418376d009' }))));
210
+ return (index.h("div", { key: 'd69fccefc43e1fbf8a2e97dce079634b2113f988', class: "bs-popover" }, index.h("div", { key: 'b42473957daa2f3db84c59f6da2e391b8387df23', part: "trigger", class: "bs-popover__trigger", onClick: this.onTriggerClick, onKeyDown: this.onTriggerKeyDown }, index.h("slot", { key: '899294fecb6b2912c3c4f8865893e62ba74cc4fb', name: "trigger", ref: el => (this.triggerSlot = el), onSlotchange: this.syncTriggerAria })), index.h("div", { key: 'e1e6209b661a16954b9b61586624dea57efa2c08', part: "popup", id: this.popupId, class: "bs-popover__popup", tabindex: "-1", hidden: !this.open && !this.closing, ref: el => (this.popupEl = el) }, index.h("slot", { key: '22e665b595f9dd2938919da88de45e0016f4a0e2' }))));
187
211
  }
188
212
  static get watchers() { return {
189
213
  "open": [{
@@ -39,7 +39,7 @@ const BsSearchItem = class {
39
39
  slotFilled = (set) => (ev) => set(ev.target.assignedNodes().length > 0);
40
40
  render() {
41
41
  const role = this.selectionRole ?? undefined;
42
- return (index.h("div", { key: '4e02263a2d377be0989f3469555f19f9b4933840', part: "container", class: "bs-search-item", role: role, "aria-selected": role ? String(this.active) : undefined, "aria-disabled": role && this.disabled ? 'true' : undefined }, !this.disabled && index.h("bs-ripple", { key: '00523f734a7d994cf448df045a73d4588da1c00c', part: "ripple" }), index.h("span", { key: '2d030afeec12e555c72d1659f0ac7d2ecb4df70a', part: "icon", class: { 'bs-search-item__icon': true, 'bs-search-item__icon--hidden': !this.hasIcon } }, index.h("slot", { key: 'ab14918cfcb5425b33c1489232ea18bf940dc49d', name: "icon", onSlotchange: this.slotFilled(v => (this.hasIcon = v)) })), index.h("span", { key: 'eee1f17e15fa4195f3a65fa7bc7f3ad12d839b7c', part: "content", class: "bs-search-item__content" }, index.h("span", { key: '75cd5ada24d085dce96331782c4e71317d0d8ed4', part: "label", class: "bs-search-item__label" }, index.h("slot", { key: '47fbf5d269d2a23eb824ba32084886efdad2aa96' })), index.h("span", { key: 'b2e4154147a02059d08505bc9f452931bbcff2bf', part: "description", class: { 'bs-search-item__description': true, 'bs-search-item__description--hidden': !this.hasDescription } }, index.h("slot", { key: '8b72f89b59fa15c21c0d02b3db88b3e291d72f81', name: "description", onSlotchange: this.slotFilled(v => (this.hasDescription = v)) }))), index.h("span", { key: 'e0b5675b2d71fe5e740fd90ff2aea5eecca2f9eb', part: "trailing", class: { 'bs-search-item__trailing': true, 'bs-search-item__trailing--hidden': !this.hasTrailingSlot && !this.trailing } }, index.h("slot", { key: 'c0dc999d7b4da6b670690b72071f5b844b86b35b', name: "trailing", onSlotchange: this.slotFilled(v => (this.hasTrailingSlot = v)) }, this.trailing))));
42
+ return (index.h("div", { key: 'd06ef043c3fa15df8d3adf3ff62b105dcef5dc1d', part: "container", class: "bs-search-item", role: role, "aria-selected": role ? String(this.active) : undefined, "aria-disabled": this.disabled ? 'true' : undefined }, !this.disabled && index.h("bs-ripple", { key: 'a9dbae40775340f256d9ac2dd54c43f7144dda41', part: "ripple" }), index.h("span", { key: '2b80eb34ad0901c6e179770807cca6cd306acd25', part: "icon", class: { 'bs-search-item__icon': true, 'bs-search-item__icon--hidden': !this.hasIcon } }, index.h("slot", { key: '7fc3bef2ba29179b425b2f470c61b6c70dae01a2', name: "icon", onSlotchange: this.slotFilled(v => (this.hasIcon = v)) })), index.h("span", { key: '67481270ed566846d87cf12325db53d44f2230d1', part: "content", class: "bs-search-item__content" }, index.h("span", { key: 'e5993e5796e55fcf03def1d33787fe43a8b652f6', part: "label", class: "bs-search-item__label" }, index.h("slot", { key: '8aab4906967341b2938a5ec5ecab5794139a4acf' })), index.h("span", { key: '8a9453d5303e6b89c3abef8864e536726cea9906', part: "description", class: { 'bs-search-item__description': true, 'bs-search-item__description--hidden': !this.hasDescription } }, index.h("slot", { key: '7f2ef31966d836fbef03d5e4610263081cab87b5', name: "description", onSlotchange: this.slotFilled(v => (this.hasDescription = v)) }))), index.h("span", { key: 'fcac22f41850199ef148124f9ddbdb3b687418a9', part: "trailing", class: { 'bs-search-item__trailing': true, 'bs-search-item__trailing--hidden': !this.hasTrailingSlot && !this.trailing } }, index.h("slot", { key: '19167fcf2502d2a16f36f3620f3257a3cd5f5cf3', name: "trailing", onSlotchange: this.slotFilled(v => (this.hasTrailingSlot = v)) }, this.trailing))));
43
43
  }
44
44
  };
45
45
  BsSearchItem.style = bsSearchItemCss();
@@ -19,7 +19,7 @@ const BsShortcutRow = class {
19
19
  /** Set by the parent list (`"listitem"`). Standalone rows have no role. */
20
20
  selectionRole = null;
21
21
  render() {
22
- return (index.h("div", { key: 'e7f79909833204fb269402972500a9d97401665d', part: "container", class: "bs-shortcut-row", role: this.selectionRole ?? undefined }, index.h("span", { key: '68dae2196bda22cd09bbc0b07958f8f2be66e332', part: "action", class: "bs-shortcut-row__action" }, index.h("slot", { key: '892a107840f1861f3c4ef5ae03e781839dead497' }, this.action)), index.h("span", { key: '06637079e74f4c944984133ab5cd897b40d64fd1', part: "keys", class: "bs-shortcut-row__keys" }, index.h("slot", { key: 'a21d5b51332d383a8d3670a0531e536f240c8e09', name: "keys" }, index.h("bs-key-caps", { key: '231389f5cf6d712a14b68a84bd9c02ce65be0ff7', keys: this.keys, label: this.label, disabled: this.disabled })))));
22
+ return (index.h("div", { key: '01234047de01effc5d7ae411a6180d02e663d9af', part: "container", class: "bs-shortcut-row", role: this.selectionRole ?? undefined, "aria-disabled": this.disabled ? 'true' : undefined }, index.h("span", { key: 'd8c80f480f4f463f5c0ee46719e4b051e1f54816', part: "action", class: "bs-shortcut-row__action" }, index.h("slot", { key: '8c5da35b9c05f1544892808b75b4b24701554dc7' }, this.action)), index.h("span", { key: '3588f962523eabfdbebc2de3d8cdd3cface4ca07', part: "keys", class: "bs-shortcut-row__keys" }, index.h("slot", { key: '3315142746d44c8c27ce6952a5cffad1f51473d8', name: "keys" }, index.h("bs-key-caps", { key: '70a4155ce7ed5b0e585ab45e9f2a8b1c69880e4e', keys: this.keys, label: this.label, disabled: this.disabled })))));
23
23
  }
24
24
  };
25
25
  BsShortcutRow.style = bsShortcutRowCss();
@@ -223,7 +223,7 @@ const BsSlider = class {
223
223
  render() {
224
224
  const isRange = this.type === 'range';
225
225
  const rootClasses = ['bs-slider', this.disabled ? 'bs-slider--disabled' : ''].filter(Boolean).join(' ');
226
- return (index.h("div", { key: '242e99440bf989ab9e8370772a415ef98ea4df97', class: rootClasses }, this.showLabel && (index.h("div", { key: '23063ed9dd2dfefc2df1ae8794fdb6d54195ee39', class: "bs-slider__header" }, index.h("span", { key: '15debc72803ed291564d1573179e9e30fd4a40f6', id: "bs-slider-label", part: "label", class: "bs-slider__label" }, this.label), isRange ? (index.h("span", { class: "bs-slider__value-fields" }, this.renderPercentField(this.percent(this.effectiveValueStart), this.onStartPercentFieldChange, this.ariaLabel ?? this.startValueLabel), index.h("span", { class: "bs-slider__separator" }, "\u2013"), this.renderPercentField(this.percent(this.effectiveValueEnd), this.onEndPercentFieldChange, this.ariaLabel ? `${this.ariaLabel} end` : this.endValueLabel))) : (this.renderPercentField(this.percent(this.value), this.onPercentFieldChange, this.ariaLabel ? `${this.ariaLabel} value` : 'Value')))), index.h("div", { key: 'b975a8200846dbd43622473d3e62255c805e0359', class: "bs-slider__row" }, this.showLabel && (index.h("span", { key: 'c82b225aa14924963005a578fdaf0ab1accf8c2b', part: "bound", class: "bs-slider__bound" }, this.min)), isRange ? this.renderRangeThumbs() : this.renderSingleThumb(), this.showLabel && (index.h("span", { key: '5e85c40cb85d2b71591020249cac4d5ae0c300a4', part: "bound", class: "bs-slider__bound" }, this.max)))));
226
+ return (index.h("div", { key: 'edb31a417eff920fb2fbff7cd26620629038449b', class: rootClasses, "aria-disabled": this.disabled ? 'true' : undefined }, this.showLabel && (index.h("div", { key: '8732ee01cd1441c0a715b45257ac560b062fc384', class: "bs-slider__header" }, index.h("span", { key: '25440a9c9bee46d3c82c1eda056e24b7e92e8302', id: "bs-slider-label", part: "label", class: "bs-slider__label" }, this.label), isRange ? (index.h("span", { class: "bs-slider__value-fields" }, this.renderPercentField(this.percent(this.effectiveValueStart), this.onStartPercentFieldChange, this.ariaLabel ?? this.startValueLabel), index.h("span", { class: "bs-slider__separator" }, "\u2013"), this.renderPercentField(this.percent(this.effectiveValueEnd), this.onEndPercentFieldChange, this.ariaLabel ? `${this.ariaLabel} end` : this.endValueLabel))) : (this.renderPercentField(this.percent(this.value), this.onPercentFieldChange, this.ariaLabel ? `${this.ariaLabel} value` : 'Value')))), index.h("div", { key: '9fcd6e0729266f1c38719347f6b48590788fb8c5', class: "bs-slider__row" }, this.showLabel && (index.h("span", { key: 'c55a68e9244d1ab2102d6c90d31b35df49f6119c', part: "bound", class: "bs-slider__bound" }, this.min)), isRange ? this.renderRangeThumbs() : this.renderSingleThumb(), this.showLabel && (index.h("span", { key: '1140fda48f69433161dc2fd1e65f18d1f09f2ed9', part: "bound", class: "bs-slider__bound" }, this.max)))));
227
227
  }
228
228
  static get delegatesFocus() { return true; }
229
229
  static get formAssociated() { return true; }
@@ -34,7 +34,7 @@ const BsStepperStep = class {
34
34
  }
35
35
  render() {
36
36
  const state = this.state;
37
- return (index.h(index.Host, { key: '85fbebb638a7e323d2e524b74c1077c261c00066', role: "listitem", class: `bs-stepper-step--${this.direction}` }, index.h("div", { key: 'a44a6b58bf74b27cee279c761ed596373882791a', class: "bs-stepper-step__top" }, index.h("span", { key: 'ae7045a3049b42d245292960fa8eb3f7dfe61864', part: "icon", class: `bs-stepper-step__icon bs-stepper-step__icon--${state}` }, state === 'current' && index.h("span", { key: '1ff646a4f8fbb2f81b2208086d280c183ddba61c', class: "bs-stepper-step__icon-dot" }), state === 'done' && index.h(CheckIcon, { key: '9fbe0328c61ac5108b22be369c1db1652523eb00' }), state === 'error' && index.h(ExclamationIcon, { key: 'ea56eb9dc52b9d1b9070ef273d46fece166c362d' })), index.h("span", { key: '0c4b5fde931c626e2b3652e7f047c1ac21b51f02', part: "connector", class: "bs-stepper-step__connector", "aria-hidden": "true" }, index.h("span", { key: '06a69e6f61bb744be078feee462fe293947326fa', class: "bs-stepper-step__connector-line" }))), index.h("span", { key: '982de1069e1eb3e409ac81600f4fc3c7512fe190', part: "label", class: `bs-stepper-step__label bs-stepper-step__label--${state}` }, index.h("span", { key: 'f946599b4130219b91e76aec9ab6442bf08bd901', part: "name", class: "bs-stepper-step__name" }, this.name), this.showDescription && this.description && (index.h("span", { key: '043581dd2db1ffc8fae23ebc32a5346ba5bf325e', part: "description", class: "bs-stepper-step__description" }, this.description)))));
37
+ return (index.h(index.Host, { key: 'a413a500376629d039afab3168b83cae5c636dc1', role: "listitem", class: `bs-stepper-step--${this.direction}`, "aria-disabled": state === 'disabled' ? 'true' : undefined }, index.h("div", { key: '7978cd670eeafe2dee95a2e3d97625ff22cd006d', class: "bs-stepper-step__top" }, index.h("span", { key: '0d0e5ef0199013cd0ed59b26fe169c2bac727920', part: "icon", class: `bs-stepper-step__icon bs-stepper-step__icon--${state}` }, state === 'current' && index.h("span", { key: '6c9ab59838f63958a54b852c4a8f3babf1ddc48e', class: "bs-stepper-step__icon-dot" }), state === 'done' && index.h(CheckIcon, { key: '284d95f3e0ae3033ab2baa079c00cb2dc1fbcd3f' }), state === 'error' && index.h(ExclamationIcon, { key: '45b894a13cf54b0d5cb027d89646c712584bf6bb' })), index.h("span", { key: '79621665f9aecb82c4135ac2e1139ce1ee9af6be', part: "connector", class: "bs-stepper-step__connector", "aria-hidden": "true" }, index.h("span", { key: '91454b553634d90734dbd2b8bb9ab53d9bead49c', class: "bs-stepper-step__connector-line" }))), index.h("span", { key: 'd5f1606511df9745571d96cad36d38774fd943b4', part: "label", class: `bs-stepper-step__label bs-stepper-step__label--${state}` }, index.h("span", { key: '81df6e0b9607c22cfdad7e37095d245ec428e0f1', part: "name", class: "bs-stepper-step__name" }, this.name), this.showDescription && this.description && (index.h("span", { key: '77c4951d570219071e8acedc361a73eed6a89612', part: "description", class: "bs-stepper-step__description" }, this.description)))));
38
38
  }
39
39
  };
40
40
  const CheckIcon = () => (index.h("svg", { viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true" }, index.h("path", { d: "M2.5 9L6 12.5L14 4.5", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" })));
@@ -113,7 +113,7 @@ export class BsAccordion {
113
113
  // `<div role="button">`: it gets click, keyboard activation (Enter/Space), focus-visible
114
114
  // styling, and `disabled` semantics (not focusable, no events) for free, and flexbox handles
115
115
  // laying out its children as easily as a <div>'s -- no layout reason to hand-roll any of that.
116
- return (h("div", { key: 'b7645b3be5116d906a2aa6e4e680859a9c19323f', part: "container", class: `bs-accordion bs-accordion--${this.size} ${this.disabled ? 'bs-accordion--disabled' : ''}` }, h("button", { key: 'a1c824c54a529b2214dbb47cb93adea5cdadee84', type: "button", part: "header", class: `bs-accordion__header bs-accordion__header--${this.size} ${this.showIcon ? '' : 'bs-accordion__header--no-icon'}`, disabled: this.disabled, "aria-expanded": String(this.expanded), onClick: this.onActivate }, this.showIcon && (h("span", { key: '5d35f4e2c05704a6bef532919301e765f8195aa7', part: "icon", class: `bs-accordion__icon bs-accordion__icon--${this.size}`, "aria-hidden": "true" }, h("slot", { key: '137280692f375379ef43be918094192d6030e2ad', name: "icon" }, h(PlusIcon, { key: '8826de2813774382dcf21e856288ff936eb12425' })))), h("span", { key: '57f370d082a86b7488bf309a114b82077d1f6711', part: "label", class: `bs-accordion__label ${this.showIcon ? '' : 'bs-accordion__label--no-icon'}` }, h("slot", { key: '5563063582ee2e1fb79d02acdc3d0feb5ac07eec', name: "label" })), h("span", { key: '8d8afd4433823aa8122b2ed167fd5ed3c32dc21a', part: "caret", class: `bs-accordion__caret bs-accordion__caret--${this.size} ${this.expanded ? 'bs-accordion__caret--open' : ''}`, "aria-hidden": "true" }, h(CaretDownIcon, { key: '51dfcbdfa5ccff5c3e6a6c2f983fc4d7fb460c18' }))), (this.expanded || this.showPreview || this.closing) && (h("div", { key: '8d62decaf3dc7a98f91eaea1723fc26a8fc520a4', ref: el => (this.contentEl = el), part: "content", class: `bs-accordion__content ${this.showIcon ? '' : 'bs-accordion__content--no-icon'} ${this.expanded ? 'bs-accordion__content--expanded' : 'bs-accordion__content--collapsed'}` }, h("slot", { key: '56493eb5b425134b38ac60f481219a823379626e' })))));
116
+ return (h("div", { key: '5b7922ce5d634a43d1ca3020b02e61b2749646f1', part: "container", class: `bs-accordion bs-accordion--${this.size} ${this.disabled ? 'bs-accordion--disabled' : ''}`, "aria-disabled": this.disabled ? 'true' : undefined }, h("button", { key: '36a5a7504c9d4301db2268652037a28cee8cc395', type: "button", part: "header", class: `bs-accordion__header bs-accordion__header--${this.size} ${this.showIcon ? '' : 'bs-accordion__header--no-icon'}`, disabled: this.disabled, "aria-expanded": String(this.expanded), onClick: this.onActivate }, this.showIcon && (h("span", { key: '2933f14e71ec7902e310761481c7408aa5afdb07', part: "icon", class: `bs-accordion__icon bs-accordion__icon--${this.size}`, "aria-hidden": "true" }, h("slot", { key: 'd156fc7944f2177764a5622c6753c45190176549', name: "icon" }, h(PlusIcon, { key: 'c570019c3b65661d9237c9d182ab5b0aa6fcc808' })))), h("span", { key: 'dafef9038d6d29fa3193515d605244d371efc2bb', part: "label", class: `bs-accordion__label ${this.showIcon ? '' : 'bs-accordion__label--no-icon'}` }, h("slot", { key: 'c752f678a8032bd3cb01b1d9d43eb9d863707a67', name: "label" })), h("span", { key: '20fd3835f885ad26ebfea64c02bd16dfd0c3fea1', part: "caret", class: `bs-accordion__caret bs-accordion__caret--${this.size} ${this.expanded ? 'bs-accordion__caret--open' : ''}`, "aria-hidden": "true" }, h(CaretDownIcon, { key: 'af7922ee4c4280e17dddd51f6681415c70389ef6' }))), (this.expanded || this.showPreview || this.closing) && (h("div", { key: 'ac8fb07634032e1035e88c0f713841d9276df2f8', ref: el => (this.contentEl = el), part: "content", class: `bs-accordion__content ${this.showIcon ? '' : 'bs-accordion__content--no-icon'} ${this.expanded ? 'bs-accordion__content--expanded' : 'bs-accordion__content--collapsed'}` }, h("slot", { key: '6263559f2a03fd514cdc98538e1c9faee0767313' })))));
117
117
  }
118
118
  static get is() { return "bs-accordion"; }
119
119
  static get encapsulation() { return "shadow"; }
@@ -28,7 +28,7 @@ import { h } from "@stencil/core";
28
28
  * - A strict sequence of steps -- use `bs-stepper`.
29
29
  * - Paging through a large, addressable result set -- use `bs-pagination`.
30
30
  *
31
- * @slot - Default slot: the slides. Each direct child is one slide.
31
+ * @slot - Default slot: the slides. Each direct child is one slide and gets `role="group"`, so wrap an image in an element (don't slot a bare `<img>`).
32
32
  * @part carousel - The outer region (`role="region"`, `aria-roledescription="carousel"`).
33
33
  * @part slides - The row holding the visible slides.
34
34
  * @part tracker - The row holding the tracker and the Pause/Play button.
@@ -257,7 +257,7 @@ export class BsCarousel {
257
257
  // While auto-advance is actually running, slide changes must not be announced (it would
258
258
  // interrupt the user constantly); otherwise announce them politely.
259
259
  const rotating = this.autoplay && !this.paused && !this.interacting;
260
- return (h("section", { key: 'fe38898b10611d2bb20c3428cbc4009449186453', part: "carousel", class: "bs-carousel", role: "region", "aria-roledescription": "carousel", "aria-label": this.label, style: { '--bs-carousel-slides-per-view': String(this.perView) } }, h("div", { key: 'd1d01494417dc282a52931307406970b8a1b7011', part: "slides", class: "bs-carousel__slides", "aria-live": rotating ? 'off' : 'polite' }, h("slot", { key: '955bb5229671a23babeea85b7f23b5ec3b330ebe', onSlotchange: this.onSlotChange })), showTracker && (h("div", { key: '7c4c3e73bc8dbf05e351cb0628e26e138d0a4028', part: "tracker", class: "bs-carousel__tracker" }, this.tracker === 'numbers' ? (h("bs-pagination", { part: "pagination", totalPages: this.pageCount, currentPage: this.page, onBsPageChange: (ev) => {
260
+ return (h("section", { key: 'cc9bb48b594809276ba1d7a37bdab86e6406ce4b', part: "carousel", class: "bs-carousel", role: "region", "aria-roledescription": "carousel", "aria-label": this.label, style: { '--bs-carousel-slides-per-view': String(this.perView) } }, h("div", { key: 'd6505ef1cf25ba7ad7f43e733c72324e54138882', part: "slides", class: "bs-carousel__slides", "aria-live": rotating ? 'off' : 'polite' }, h("slot", { key: 'd619f41a54d346f05e56dae300fc8e6f8e0d9161', onSlotchange: this.onSlotChange })), showTracker && (h("div", { key: '4cc21ab05421bdba11a2588a6628041f8ef57e64', part: "tracker", class: "bs-carousel__tracker" }, this.tracker === 'numbers' ? (h("bs-pagination", { part: "pagination", totalPages: this.pageCount, currentPage: this.page, onBsPageChange: (ev) => {
261
261
  ev.stopPropagation();
262
262
  this.goTo(ev.detail);
263
263
  } })) : (h("div", { class: "bs-carousel__nav" }, this.renderDotsTracker())), this.renderPause()))));
@@ -19,12 +19,16 @@ const PHOTO_IDS = [
19
19
  'photo-1512917774080-9991f1c4c750',
20
20
  'photo-1660496247667-3fb697c396af',
21
21
  ];
22
+ // Each slide is a wrapper <div>, not a bare <img>: the carousel gives every slide role="group" and an
23
+ // "n of N" label, which is invalid on an <img> (and would replace its alt text).
22
24
  const slot = (n) => html `
23
- <img
24
- src="https://images.unsplash.com/${PHOTO_IDS[(n - 1) % PHOTO_IDS.length]}?fm=jpg&q=80&w=640&h=360&fit=crop&auto=format"
25
- alt="Landscape photograph ${n}"
26
- style="display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover; border-radius: var(--bs-border-radius-100);"
27
- />
25
+ <div>
26
+ <img
27
+ src="https://images.unsplash.com/${PHOTO_IDS[(n - 1) % PHOTO_IDS.length]}?fm=jpg&q=80&w=640&h=360&fit=crop&auto=format"
28
+ alt="Landscape photograph ${n}"
29
+ style="display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover; border-radius: var(--bs-border-radius-100);"
30
+ />
31
+ </div>
28
32
  `;
29
33
  const meta = {
30
34
  title: 'Components/bs-carousel',
@@ -44,7 +44,7 @@ export class BsChipInformative {
44
44
  };
45
45
  render() {
46
46
  const paddingVariant = this.hasIcon ? 'icon' : 'text';
47
- return (h(Host, { key: '300d18219fd28709d023bf5d8884ecd7365eb259', class: `bs-chip-informative bs-chip-informative--${this.color} bs-chip-informative--${this.size} bs-chip-informative--${paddingVariant}` }, h("span", { key: '9e75b6aa312abd6433dd83af37f27587cee98590', part: "icon", class: `bs-chip-informative__icon ${this.hasIcon ? 'bs-chip-informative__icon--visible' : ''}` }, h("slot", { key: 'cc55742c7d920b535efb6c3e2f721a88172dbe68', name: "icon", onSlotchange: this.onIconSlotchange })), h("span", { key: '198316a11fa1fd0a2eeca15d138f92bb00a5de6b', part: "label", class: "bs-chip-informative__label" }, h("slot", { key: '346399cea299338a6110e34fe9a7130e99aa6c09' }))));
47
+ return (h(Host, { key: '859a7b1d4d42f8dcb3954b66d013c588453a2dfd', class: `bs-chip-informative bs-chip-informative--${this.color} bs-chip-informative--${this.size} bs-chip-informative--${paddingVariant}`, "aria-disabled": this.disabled ? 'true' : undefined }, h("span", { key: 'f9d97bc62eea33379adb890e004ecd0a2a8c5c3d', part: "icon", class: `bs-chip-informative__icon ${this.hasIcon ? 'bs-chip-informative__icon--visible' : ''}` }, h("slot", { key: '4e41027828c2514bfdc68a1e13b4bb0ca0278653', name: "icon", onSlotchange: this.onIconSlotchange })), h("span", { key: '9519ca92daaadce0572e0dccb6607390d7e019c8', part: "label", class: "bs-chip-informative__label" }, h("slot", { key: '1231db6420ffbfbb63cbba714ca51d571f5bd981' }))));
48
48
  }
49
49
  static get is() { return "bs-chip-informative"; }
50
50
  static get encapsulation() { return "shadow"; }
@@ -85,6 +85,17 @@ describe('bs-company-switcher', () => {
85
85
  expect(q(el, '[part="list"]')).toBeNull();
86
86
  expect(q(el, '[part="footer"]')).not.toBeNull();
87
87
  });
88
+ it('reports the search combobox as expanded only while there is a listbox to control', async () => {
89
+ const { el, type } = await make({ open: true });
90
+ const search = q(el, '[part="search"]');
91
+ expect(search.getAttribute('aria-expanded')).toBe('true');
92
+ expect(search.getAttribute('aria-controls')).toBe(q(el, '[part="list"]').id);
93
+ await type('zzz');
94
+ expect(search.getAttribute('aria-expanded')).toBe('false');
95
+ expect(search.hasAttribute('aria-controls')).toBe(false);
96
+ await type('');
97
+ expect(search.getAttribute('aria-expanded')).toBe('true');
98
+ });
88
99
  it('navigates with arrow keys and switches with Enter', async () => {
89
100
  const { el, press, waitForChanges } = await make({ open: true });
90
101
  await settle();
@@ -157,15 +168,10 @@ describe('bs-company-switcher', () => {
157
168
  expect(q(el, 'bs-input').placeholder).toBe('Finn selskap');
158
169
  expect(q(el, '[part="footer"]').textContent).toContain('Administrer');
159
170
  });
160
- it('has no axe violations open (apart from bs-popover\'s own trigger wrapper)', async () => {
171
+ it('has no axe violations open', async () => {
161
172
  const { el } = await make({ open: true });
162
- // Known gap in bs-popover: its trigger wrapper <div> carries aria-expanded/haspopup without a role
163
- // (bs-popover's own tests assert those attributes), so axe flags aria-allowed-attr on it.
164
173
  const results = await axe.run(el);
165
- const violations = results.violations
166
- .map(v => ({ ...v, nodes: v.nodes.filter(n => !n.html.includes('bs-popover__trigger')) }))
167
- .filter(v => v.nodes.length)
168
- .map(v => `${v.id}: ${v.help}`);
174
+ const violations = results.violations.map(v => `${v.id}: ${v.help}`);
169
175
  expect(violations).toEqual([]);
170
176
  });
171
177
  });
@@ -234,10 +234,10 @@ export class BsCompanySwitcher {
234
234
  const empty = !!this.q && groups.length === 0;
235
235
  const listId = `${this.id}-listbox`;
236
236
  const total = groups.reduce((n, g) => n + g.items.length, 0);
237
- return (h("bs-popover", { key: '5980bccab6835ad9384ba9314e35f0013053d2c9', class: "bs-company-switcher", open: this.open, placement: "bottom-start", onBsOpenChange: this.onPopoverOpenChange }, this.renderTrigger(), h("bs-card", { key: '6afff3828ecd224660d171514c23e7b3ca30f7ee', part: "panel", class: "bs-company-switcher__panel", surface: "base" }, h("div", { key: 'a046f869f51c0024e26efabf572dff82a8e2f787', class: "bs-company-switcher__search", onMouseDown: (ev) => ev.preventDefault() }, h("div", { key: '90e931c5129383fdb6443dae3ff9bed40fe61935', part: "search", role: "combobox", "aria-label": this.searchPlaceholder, "aria-expanded": "true", "aria-haspopup": "listbox", "aria-controls": groups.length ? listId : undefined, "aria-activedescendant": this.activeId ?? undefined, onKeyDown: this.onKeyDown }, h("bs-input", { key: 'c52a69879cdf3e7053f8b2fe97b5954b443121b6', ref: el => (this.inputEl = el), type: "search", value: this.query, placeholder: this.searchPlaceholder, onBsInput: this.onInput }))), groups.length > 0 && (h("div", { key: 'fd72b97de3388bb607abab5ec55fe5602314f281', id: listId, part: "list", class: "bs-company-switcher__list", role: "listbox", "aria-label": this.listLabel, onMouseDown: (ev) => ev.preventDefault(), onMouseLeave: () => (this.activeId = null) }, groups.map(g => {
237
+ return (h("bs-popover", { key: '5980bccab6835ad9384ba9314e35f0013053d2c9', class: "bs-company-switcher", open: this.open, placement: "bottom-start", onBsOpenChange: this.onPopoverOpenChange }, this.renderTrigger(), h("bs-card", { key: '6afff3828ecd224660d171514c23e7b3ca30f7ee', part: "panel", class: "bs-company-switcher__panel", surface: "base" }, h("div", { key: 'a046f869f51c0024e26efabf572dff82a8e2f787', class: "bs-company-switcher__search", onMouseDown: (ev) => ev.preventDefault() }, h("div", { key: '9cbdbdc07a5c673c21bb2af85d0e22d941278cc7', part: "search", role: "combobox", "aria-label": this.searchPlaceholder, "aria-expanded": groups.length ? 'true' : 'false', "aria-haspopup": "listbox", "aria-controls": groups.length ? listId : undefined, "aria-activedescendant": this.activeId ?? undefined, onKeyDown: this.onKeyDown }, h("bs-input", { key: 'd48151ab5c7b3c52bd5272b276ed65b050ba8a86', ref: el => (this.inputEl = el), type: "search", value: this.query, placeholder: this.searchPlaceholder, onBsInput: this.onInput }))), groups.length > 0 && (h("div", { key: 'd7cbca7fbfea209e5063e997e0277e72833bae6e', id: listId, part: "list", class: "bs-company-switcher__list", role: "listbox", "aria-label": this.listLabel, onMouseDown: (ev) => ev.preventDefault(), onMouseLeave: () => (this.activeId = null) }, groups.map(g => {
238
238
  const hid = `${this.id}-h-${g.key}`;
239
239
  return (h("div", { part: "group", class: "bs-company-switcher__group", role: "group", "aria-labelledby": hid }, h("div", { part: "group-header", id: hid, class: "bs-company-switcher__group-header" }, g.header), g.items.map(c => this.renderRow(c))));
240
- }))), empty && (h("bs-empty-state", { key: 'ec7b430654c356c6746c66e416e67ee436b1fdd0', part: "empty", class: "bs-company-switcher__empty", heading: this.emptyHeading.replace('{query}', this.query.trim()), description: this.emptyDescription }, h("svg", { key: '5a40d259338fabb61f69ebdcb0e3a9940f351ffe', slot: "icon", ...svgProps }, h("circle", { key: '757a039bb1b483c08f08af14e1b14dba2ae68c88', cx: "11", cy: "11", r: "7" }), h("path", { key: '5271a517de56a069843454c4f58c559ad458e20e', d: "m20 20-3.5-3.5" })))), h("bs-divider", { key: 'd5c25f9dc46b3272a7734ccc8b8d6fd5ed06986c', class: "bs-company-switcher__divider", decorative: true }), h("div", { key: 'd862e4c545a508f5306c07b76e92b1b381b68bc7', part: "footer", class: "bs-company-switcher__footer" }, h("bs-button", { key: '319614a523516d33c33cd6245c74787374e5304f', variant: "subtle", size: "sm", onClick: () => this.bsManageCompanies.emit() }, h("svg", { key: 'fd832a30f82be7bd8c61e236ecb605e12e2a1c0c', slot: "icon", ...svgProps }, h("circle", { key: '4efb646b40beb60f8b9eda1ea0204e2940c5272d', cx: "12", cy: "12", r: "3" }), h("path", { key: '199d7121c6ae29d435daa2c9e7c13af9f4415b19', d: "M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1Z" })), this.manageLabel), h("bs-button", { key: 'b5c5f44bdbde03b5192c1aba73fffd22424594a9', variant: "subtle", size: "sm", onClick: () => this.bsAddCompanies.emit() }, h("svg", { key: 'c5ba249024a7fdd303cea0c94ad44befd17be2b1', slot: "icon", ...svgProps }, h("path", { key: 'a1b62a0f772ec9f89126d70ac0bc18c76208d6e3', d: "M12 5v14M5 12h14" })), this.addLabel)), h("span", { key: '1a23f13dcd3eba63ba9d69e54b6311bbc622a5d1', class: "bs-company-switcher__live", "aria-live": "polite" }, this.open && this.q ? (empty ? this.emptyHeading.replace('{query}', this.query.trim()) : `${total} ${total === 1 ? this.matchLabel : this.matchesLabel}`) : ''))));
240
+ }))), empty && (h("bs-empty-state", { key: '5dbff2f753d480fb8e3633ceed77ee4b746e96af', part: "empty", class: "bs-company-switcher__empty", heading: this.emptyHeading.replace('{query}', this.query.trim()), description: this.emptyDescription }, h("svg", { key: 'b179aa273060b4f8b2c4e07844d6616f33f3985d', slot: "icon", ...svgProps }, h("circle", { key: 'd48847aaaea92eb7e6af0c39285cfa445f508114', cx: "11", cy: "11", r: "7" }), h("path", { key: '9b5f6c641c50a6d280c149da5f70871ee2d85032', d: "m20 20-3.5-3.5" })))), h("bs-divider", { key: '25bee440e7b15f68b3f451ef669c2efaae26c400', class: "bs-company-switcher__divider", decorative: true }), h("div", { key: '2c29e55970179036fc837352f5112bbc5b3b5429', part: "footer", class: "bs-company-switcher__footer" }, h("bs-button", { key: '34a9b9188c87c2c335a7eaf96fd3e0b445d1f374', variant: "subtle", size: "sm", onClick: () => this.bsManageCompanies.emit() }, h("svg", { key: '14277686d69b74c005f62ce0d483b1f6ef70ee7e', slot: "icon", ...svgProps }, h("circle", { key: '98423b978ae2ba9d0b8e17892d6269d2baf005e1', cx: "12", cy: "12", r: "3" }), h("path", { key: 'b61815c0f8c56ca6dd9de05d5d5b58738648a08d', d: "M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1Z" })), this.manageLabel), h("bs-button", { key: '4c83a67e7f54352e839b84b23a4aeb3b74ea26af', variant: "subtle", size: "sm", onClick: () => this.bsAddCompanies.emit() }, h("svg", { key: 'e504aeb192774bd8093e04c0795bd3ab700596a8', slot: "icon", ...svgProps }, h("path", { key: '0806d38b7b739308432dff3f71696cd92923f4ca', d: "M12 5v14M5 12h14" })), this.addLabel)), h("span", { key: 'e7ef671da5d9ac2431cd8f27dcc30985046946ac', class: "bs-company-switcher__live", "aria-live": "polite" }, this.open && this.q ? (empty ? this.emptyHeading.replace('{query}', this.query.trim()) : `${total} ${total === 1 ? this.matchLabel : this.matchesLabel}`) : ''))));
241
241
  }
242
242
  static get is() { return "bs-company-switcher"; }
243
243
  static get encapsulation() { return "shadow"; }
@@ -62,7 +62,7 @@ export const AllStates = {
62
62
  <h4 style="margin:0; font-family: sans-serif; text-transform: capitalize;">${variant}</h4>
63
63
  ${states.map(state => html `
64
64
  <div>
65
- <div style="font-size: 12px; font-family: monospace; color: #888; margin-bottom: 4px;">${state}</div>
65
+ <div style="font-size: 12px; font-family: monospace; color: var(--bs-text-secondary); margin-bottom: 4px;">${state}</div>
66
66
  <bs-composer
67
67
  variant=${variant}
68
68
  state=${state}
@@ -127,9 +127,7 @@ describe('bs-dropdown', () => {
127
127
  it('passes axe when open', async () => {
128
128
  const { root, waitForChanges } = await setup({ open: true });
129
129
  await settle(waitForChanges);
130
- // bs-popover puts aria-haspopup/aria-expanded on its role-less trigger wrapper div (pre-existing, flagged
131
- // by aria-allowed-attr); every other rule must pass.
132
- const results = await axe.run(root, { rules: { 'aria-allowed-attr': { enabled: false } } });
130
+ const results = await axe.run(root);
133
131
  expect(results.violations.map(v => v.id)).toEqual([]);
134
132
  });
135
133
  });
@@ -1,32 +1,36 @@
1
1
  import { html } from "lit";
2
2
  import { componentDescription } from "../../stories-utils";
3
+ // A bs-menu-item is role="menuitem", which must sit inside a role="menu" -- a bare `div` stands in for
4
+ // bs-menu here so the item is shown on its own, without the menu's surface and padding.
3
5
  const meta = {
4
6
  title: 'Components/bs-menu-item',
5
7
  parameters: { docs: { description: { component: componentDescription('bs-menu-item') } } },
6
- render: () => html ` <bs-menu-item style="width: 210px;">Read aloud</bs-menu-item> `,
8
+ render: () => html `<div role="menu"><bs-menu-item style="width: 210px;">Read aloud</bs-menu-item></div>`,
7
9
  };
8
10
  export default meta;
9
11
  export const Default = {};
10
12
  export const WithIcon = {
11
13
  name: 'With an icon',
12
14
  render: () => html `
13
- <bs-menu-item style="width: 210px;">
14
- <svg slot="icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
15
- <path
16
- 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"
17
- stroke="currentColor"
18
- stroke-width="2"
19
- stroke-linecap="round"
20
- stroke-linejoin="round"
21
- />
22
- <path d="M7.5 8.25V15.75" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
23
- </svg>
24
- Read aloud
25
- </bs-menu-item>
15
+ <div role="menu">
16
+ <bs-menu-item style="width: 210px;">
17
+ <svg slot="icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
18
+ <path
19
+ 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"
20
+ stroke="currentColor"
21
+ stroke-width="2"
22
+ stroke-linecap="round"
23
+ stroke-linejoin="round"
24
+ />
25
+ <path d="M7.5 8.25V15.75" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
26
+ </svg>
27
+ Read aloud
28
+ </bs-menu-item>
29
+ </div>
26
30
  `,
27
31
  };
28
32
  export const Disabled = {
29
- render: () => html ` <bs-menu-item style="width: 210px;" disabled>Read aloud</bs-menu-item> `,
33
+ render: () => html `<div role="menu"><bs-menu-item style="width: 210px;" disabled>Read aloud</bs-menu-item></div>`,
30
34
  };
31
35
  export const InAMenu = {
32
36
  name: 'In a bs-menu',
@@ -2,15 +2,22 @@ import { render, h, describe, it, expect } from "@stencil/vitest";
2
2
  const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
3
3
  const setup = () => render(h("bs-popover", null, h("button", { slot: "trigger" }, "Open"), h("div", null, "Content")));
4
4
  describe('bs-popover', () => {
5
- it('is closed by default with aria wiring on the trigger wrapper', async () => {
5
+ it('is closed by default with aria wiring on the slotted trigger, not the role-less wrapper', async () => {
6
6
  const { root } = await setup();
7
- const trigger = root.shadowRoot.querySelector('[part="trigger"]');
7
+ const wrapper = root.shadowRoot.querySelector('[part="trigger"]');
8
+ const trigger = root.querySelector('[slot="trigger"]');
8
9
  const popup = root.shadowRoot.querySelector('[part="popup"]');
9
10
  expect(trigger).toEqualAttribute('aria-expanded', 'false');
10
11
  expect(trigger).toEqualAttribute('aria-haspopup', 'true');
11
- expect(trigger.getAttribute('aria-controls')).toBe(popup.id);
12
+ expect(wrapper).not.toHaveAttribute('aria-expanded');
13
+ expect(wrapper).not.toHaveAttribute('aria-haspopup');
12
14
  expect(popup.hasAttribute('hidden')).toBe(true);
13
15
  });
16
+ it('keeps an aria-haspopup the trigger already declares', async () => {
17
+ const { root, waitForChanges } = await render(h("bs-popover", null, h("button", { slot: "trigger", "aria-haspopup": "menu" }, "Open"), h("div", null, "Content")));
18
+ await waitForChanges();
19
+ expect(root.querySelector('[slot="trigger"]')).toEqualAttribute('aria-haspopup', 'menu');
20
+ });
14
21
  it('toggles on trigger click and emits bsOpenChange', async () => {
15
22
  const { root, waitForChanges, spyOnEvent } = await setup();
16
23
  const spy = spyOnEvent('bsOpenChange');
@@ -19,7 +26,7 @@ describe('bs-popover', () => {
19
26
  await waitForChanges();
20
27
  expect(root.open).toBe(true);
21
28
  expect(root).toHaveAttribute('open');
22
- expect(trigger).toEqualAttribute('aria-expanded', 'true');
29
+ expect(root.querySelector('[slot="trigger"]')).toEqualAttribute('aria-expanded', 'true');
23
30
  expect(spy).toHaveReceivedEventDetail(true);
24
31
  trigger.click();
25
32
  await waitForChanges();
@@ -16,7 +16,7 @@ let popoverId = 0;
16
16
  *
17
17
  * @slot trigger - The element that toggles the popup (e.g. a `bs-button`).
18
18
  * @slot - Default slot: the popup content.
19
- * @part trigger - The wrapper around the trigger; carries aria-expanded/aria-haspopup/aria-controls.
19
+ * @part trigger - The wrapper around the trigger. `aria-haspopup`/`aria-expanded` are set on the slotted trigger element itself, not on this wrapper.
20
20
  * @part popup - The popup surface.
21
21
  * @prop --bs-popover-bg - Popup background. Aliased to `--bs-surface-raised`.
22
22
  * @prop --bs-popover-radius - Popup corner radius. Aliased to `--bs-border-radius-100`.
@@ -107,6 +107,7 @@ export class BsPopover {
107
107
  });
108
108
  }
109
109
  componentDidRender() {
110
+ this.syncTriggerAria();
110
111
  if (!this.open && this.closing && this.exitPending) {
111
112
  this.exitPending = false;
112
113
  this.playExit();
@@ -182,6 +183,29 @@ export class BsPopover {
182
183
  this.open = next;
183
184
  this.bsOpenChange.emit(next);
184
185
  }
186
+ /**
187
+ * Keeps `aria-haspopup`/`aria-expanded` on the slotted trigger -- the element that is actually
188
+ * focusable and announced -- rather than on the role-less wrapper div, where they are invalid ARIA.
189
+ * `bs-*` triggers (bs-button, bs-icon-button) forward these to their inner `<button>` through their
190
+ * JS properties; an attribute on the host would sit on a role-less element. A trigger that already
191
+ * declares its own `aria-haspopup` (e.g. bs-dropdown's `menu`) keeps it.
192
+ */
193
+ syncTriggerAria = () => {
194
+ const trigger = this.triggerSlot?.assignedElements()[0];
195
+ if (!trigger)
196
+ return;
197
+ if (trigger.tagName.startsWith('BS-')) {
198
+ const t = trigger;
199
+ if (!t.ariaHasPopup)
200
+ t.ariaHasPopup = 'true';
201
+ t.ariaExpanded = String(this.open);
202
+ }
203
+ else {
204
+ if (!trigger.hasAttribute('aria-haspopup'))
205
+ trigger.setAttribute('aria-haspopup', 'true');
206
+ trigger.setAttribute('aria-expanded', String(this.open));
207
+ }
208
+ };
185
209
  focusTrigger() {
186
210
  const first = this.triggerSlot?.assignedElements()[0];
187
211
  first?.focus();
@@ -204,7 +228,7 @@ export class BsPopover {
204
228
  }
205
229
  };
206
230
  render() {
207
- return (h("div", { key: '60037b8ebeead636ca7cfb1d1fb5656662053854', class: "bs-popover" }, h("div", { key: '216a52057acbae40f00d5c7e09ad7141fe356a3b', part: "trigger", class: "bs-popover__trigger", "aria-haspopup": "true", "aria-expanded": String(this.open), "aria-controls": this.popupId, onClick: this.onTriggerClick, onKeyDown: this.onTriggerKeyDown }, h("slot", { key: '08bd0b15dbf67de5fbfa5323570886f2ea7a5468', name: "trigger", ref: el => (this.triggerSlot = el) })), h("div", { key: '5e409b359b7afcf4da286cfb66a264897698018d', part: "popup", id: this.popupId, class: "bs-popover__popup", tabindex: "-1", hidden: !this.open && !this.closing, ref: el => (this.popupEl = el) }, h("slot", { key: '35a194b6369502fa0da50795aff5ee418376d009' }))));
231
+ return (h("div", { key: 'd69fccefc43e1fbf8a2e97dce079634b2113f988', class: "bs-popover" }, h("div", { key: 'b42473957daa2f3db84c59f6da2e391b8387df23', part: "trigger", class: "bs-popover__trigger", onClick: this.onTriggerClick, onKeyDown: this.onTriggerKeyDown }, h("slot", { key: '899294fecb6b2912c3c4f8865893e62ba74cc4fb', name: "trigger", ref: el => (this.triggerSlot = el), onSlotchange: this.syncTriggerAria })), h("div", { key: 'e1e6209b661a16954b9b61586624dea57efa2c08', part: "popup", id: this.popupId, class: "bs-popover__popup", tabindex: "-1", hidden: !this.open && !this.closing, ref: el => (this.popupEl = el) }, h("slot", { key: '22e665b595f9dd2938919da88de45e0016f4a0e2' }))));
208
232
  }
209
233
  static get is() { return "bs-popover"; }
210
234
  static get encapsulation() { return "shadow"; }
@@ -69,7 +69,7 @@ export class BsSearchItem {
69
69
  slotFilled = (set) => (ev) => set(ev.target.assignedNodes().length > 0);
70
70
  render() {
71
71
  const role = this.selectionRole ?? undefined;
72
- return (h("div", { key: '4e02263a2d377be0989f3469555f19f9b4933840', part: "container", class: "bs-search-item", role: role, "aria-selected": role ? String(this.active) : undefined, "aria-disabled": role && this.disabled ? 'true' : undefined }, !this.disabled && h("bs-ripple", { key: '00523f734a7d994cf448df045a73d4588da1c00c', part: "ripple" }), h("span", { key: '2d030afeec12e555c72d1659f0ac7d2ecb4df70a', part: "icon", class: { 'bs-search-item__icon': true, 'bs-search-item__icon--hidden': !this.hasIcon } }, h("slot", { key: 'ab14918cfcb5425b33c1489232ea18bf940dc49d', name: "icon", onSlotchange: this.slotFilled(v => (this.hasIcon = v)) })), h("span", { key: 'eee1f17e15fa4195f3a65fa7bc7f3ad12d839b7c', part: "content", class: "bs-search-item__content" }, h("span", { key: '75cd5ada24d085dce96331782c4e71317d0d8ed4', part: "label", class: "bs-search-item__label" }, h("slot", { key: '47fbf5d269d2a23eb824ba32084886efdad2aa96' })), h("span", { key: 'b2e4154147a02059d08505bc9f452931bbcff2bf', part: "description", class: { 'bs-search-item__description': true, 'bs-search-item__description--hidden': !this.hasDescription } }, h("slot", { key: '8b72f89b59fa15c21c0d02b3db88b3e291d72f81', name: "description", onSlotchange: this.slotFilled(v => (this.hasDescription = v)) }))), h("span", { key: 'e0b5675b2d71fe5e740fd90ff2aea5eecca2f9eb', part: "trailing", class: { 'bs-search-item__trailing': true, 'bs-search-item__trailing--hidden': !this.hasTrailingSlot && !this.trailing } }, h("slot", { key: 'c0dc999d7b4da6b670690b72071f5b844b86b35b', name: "trailing", onSlotchange: this.slotFilled(v => (this.hasTrailingSlot = v)) }, this.trailing))));
72
+ return (h("div", { key: 'd06ef043c3fa15df8d3adf3ff62b105dcef5dc1d', part: "container", class: "bs-search-item", role: role, "aria-selected": role ? String(this.active) : undefined, "aria-disabled": this.disabled ? 'true' : undefined }, !this.disabled && h("bs-ripple", { key: 'a9dbae40775340f256d9ac2dd54c43f7144dda41', part: "ripple" }), h("span", { key: '2b80eb34ad0901c6e179770807cca6cd306acd25', part: "icon", class: { 'bs-search-item__icon': true, 'bs-search-item__icon--hidden': !this.hasIcon } }, h("slot", { key: '7fc3bef2ba29179b425b2f470c61b6c70dae01a2', name: "icon", onSlotchange: this.slotFilled(v => (this.hasIcon = v)) })), h("span", { key: '67481270ed566846d87cf12325db53d44f2230d1', part: "content", class: "bs-search-item__content" }, h("span", { key: 'e5993e5796e55fcf03def1d33787fe43a8b652f6', part: "label", class: "bs-search-item__label" }, h("slot", { key: '8aab4906967341b2938a5ec5ecab5794139a4acf' })), h("span", { key: '8a9453d5303e6b89c3abef8864e536726cea9906', part: "description", class: { 'bs-search-item__description': true, 'bs-search-item__description--hidden': !this.hasDescription } }, h("slot", { key: '7f2ef31966d836fbef03d5e4610263081cab87b5', name: "description", onSlotchange: this.slotFilled(v => (this.hasDescription = v)) }))), h("span", { key: 'fcac22f41850199ef148124f9ddbdb3b687418a9', part: "trailing", class: { 'bs-search-item__trailing': true, 'bs-search-item__trailing--hidden': !this.hasTrailingSlot && !this.trailing } }, h("slot", { key: '19167fcf2502d2a16f36f3620f3257a3cd5f5cf3', name: "trailing", onSlotchange: this.slotFilled(v => (this.hasTrailingSlot = v)) }, this.trailing))));
73
73
  }
74
74
  static get is() { return "bs-search-item"; }
75
75
  static get encapsulation() { return "shadow"; }
@@ -36,7 +36,7 @@ export class BsShortcutRow {
36
36
  /** Set by the parent list (`"listitem"`). Standalone rows have no role. */
37
37
  selectionRole = null;
38
38
  render() {
39
- return (h("div", { key: 'e7f79909833204fb269402972500a9d97401665d', part: "container", class: "bs-shortcut-row", role: this.selectionRole ?? undefined }, h("span", { key: '68dae2196bda22cd09bbc0b07958f8f2be66e332', part: "action", class: "bs-shortcut-row__action" }, h("slot", { key: '892a107840f1861f3c4ef5ae03e781839dead497' }, this.action)), h("span", { key: '06637079e74f4c944984133ab5cd897b40d64fd1', part: "keys", class: "bs-shortcut-row__keys" }, h("slot", { key: 'a21d5b51332d383a8d3670a0531e536f240c8e09', name: "keys" }, h("bs-key-caps", { key: '231389f5cf6d712a14b68a84bd9c02ce65be0ff7', keys: this.keys, label: this.label, disabled: this.disabled })))));
39
+ return (h("div", { key: '01234047de01effc5d7ae411a6180d02e663d9af', part: "container", class: "bs-shortcut-row", role: this.selectionRole ?? undefined, "aria-disabled": this.disabled ? 'true' : undefined }, h("span", { key: 'd8c80f480f4f463f5c0ee46719e4b051e1f54816', part: "action", class: "bs-shortcut-row__action" }, h("slot", { key: '8c5da35b9c05f1544892808b75b4b24701554dc7' }, this.action)), h("span", { key: '3588f962523eabfdbebc2de3d8cdd3cface4ca07', part: "keys", class: "bs-shortcut-row__keys" }, h("slot", { key: '3315142746d44c8c27ce6952a5cffad1f51473d8', name: "keys" }, h("bs-key-caps", { key: '70a4155ce7ed5b0e585ab45e9f2a8b1c69880e4e', keys: this.keys, label: this.label, disabled: this.disabled })))));
40
40
  }
41
41
  static get is() { return "bs-shortcut-row"; }
42
42
  static get encapsulation() { return "shadow"; }
@@ -233,7 +233,7 @@ export class BsSlider {
233
233
  render() {
234
234
  const isRange = this.type === 'range';
235
235
  const rootClasses = ['bs-slider', this.disabled ? 'bs-slider--disabled' : ''].filter(Boolean).join(' ');
236
- return (h("div", { key: '242e99440bf989ab9e8370772a415ef98ea4df97', class: rootClasses }, this.showLabel && (h("div", { key: '23063ed9dd2dfefc2df1ae8794fdb6d54195ee39', class: "bs-slider__header" }, h("span", { key: '15debc72803ed291564d1573179e9e30fd4a40f6', id: "bs-slider-label", part: "label", class: "bs-slider__label" }, this.label), isRange ? (h("span", { class: "bs-slider__value-fields" }, this.renderPercentField(this.percent(this.effectiveValueStart), this.onStartPercentFieldChange, this.ariaLabel ?? this.startValueLabel), h("span", { class: "bs-slider__separator" }, "\u2013"), this.renderPercentField(this.percent(this.effectiveValueEnd), this.onEndPercentFieldChange, this.ariaLabel ? `${this.ariaLabel} end` : this.endValueLabel))) : (this.renderPercentField(this.percent(this.value), this.onPercentFieldChange, this.ariaLabel ? `${this.ariaLabel} value` : 'Value')))), h("div", { key: 'b975a8200846dbd43622473d3e62255c805e0359', class: "bs-slider__row" }, this.showLabel && (h("span", { key: 'c82b225aa14924963005a578fdaf0ab1accf8c2b', part: "bound", class: "bs-slider__bound" }, this.min)), isRange ? this.renderRangeThumbs() : this.renderSingleThumb(), this.showLabel && (h("span", { key: '5e85c40cb85d2b71591020249cac4d5ae0c300a4', part: "bound", class: "bs-slider__bound" }, this.max)))));
236
+ return (h("div", { key: 'edb31a417eff920fb2fbff7cd26620629038449b', class: rootClasses, "aria-disabled": this.disabled ? 'true' : undefined }, this.showLabel && (h("div", { key: '8732ee01cd1441c0a715b45257ac560b062fc384', class: "bs-slider__header" }, h("span", { key: '25440a9c9bee46d3c82c1eda056e24b7e92e8302', id: "bs-slider-label", part: "label", class: "bs-slider__label" }, this.label), isRange ? (h("span", { class: "bs-slider__value-fields" }, this.renderPercentField(this.percent(this.effectiveValueStart), this.onStartPercentFieldChange, this.ariaLabel ?? this.startValueLabel), h("span", { class: "bs-slider__separator" }, "\u2013"), this.renderPercentField(this.percent(this.effectiveValueEnd), this.onEndPercentFieldChange, this.ariaLabel ? `${this.ariaLabel} end` : this.endValueLabel))) : (this.renderPercentField(this.percent(this.value), this.onPercentFieldChange, this.ariaLabel ? `${this.ariaLabel} value` : 'Value')))), h("div", { key: '9fcd6e0729266f1c38719347f6b48590788fb8c5', class: "bs-slider__row" }, this.showLabel && (h("span", { key: 'c55a68e9244d1ab2102d6c90d31b35df49f6119c', part: "bound", class: "bs-slider__bound" }, this.min)), isRange ? this.renderRangeThumbs() : this.renderSingleThumb(), this.showLabel && (h("span", { key: '1140fda48f69433161dc2fd1e65f18d1f09f2ed9', part: "bound", class: "bs-slider__bound" }, this.max)))));
237
237
  }
238
238
  static get is() { return "bs-slider"; }
239
239
  static get encapsulation() { return "shadow"; }
@@ -57,7 +57,7 @@ export class BsStepperStep {
57
57
  }
58
58
  render() {
59
59
  const state = this.state;
60
- return (h(Host, { key: '85fbebb638a7e323d2e524b74c1077c261c00066', role: "listitem", class: `bs-stepper-step--${this.direction}` }, h("div", { key: 'a44a6b58bf74b27cee279c761ed596373882791a', class: "bs-stepper-step__top" }, h("span", { key: 'ae7045a3049b42d245292960fa8eb3f7dfe61864', part: "icon", class: `bs-stepper-step__icon bs-stepper-step__icon--${state}` }, state === 'current' && h("span", { key: '1ff646a4f8fbb2f81b2208086d280c183ddba61c', class: "bs-stepper-step__icon-dot" }), state === 'done' && h(CheckIcon, { key: '9fbe0328c61ac5108b22be369c1db1652523eb00' }), state === 'error' && h(ExclamationIcon, { key: 'ea56eb9dc52b9d1b9070ef273d46fece166c362d' })), h("span", { key: '0c4b5fde931c626e2b3652e7f047c1ac21b51f02', part: "connector", class: "bs-stepper-step__connector", "aria-hidden": "true" }, h("span", { key: '06a69e6f61bb744be078feee462fe293947326fa', class: "bs-stepper-step__connector-line" }))), h("span", { key: '982de1069e1eb3e409ac81600f4fc3c7512fe190', part: "label", class: `bs-stepper-step__label bs-stepper-step__label--${state}` }, h("span", { key: 'f946599b4130219b91e76aec9ab6442bf08bd901', part: "name", class: "bs-stepper-step__name" }, this.name), this.showDescription && this.description && (h("span", { key: '043581dd2db1ffc8fae23ebc32a5346ba5bf325e', part: "description", class: "bs-stepper-step__description" }, this.description)))));
60
+ return (h(Host, { key: 'a413a500376629d039afab3168b83cae5c636dc1', role: "listitem", class: `bs-stepper-step--${this.direction}`, "aria-disabled": state === 'disabled' ? 'true' : undefined }, h("div", { key: '7978cd670eeafe2dee95a2e3d97625ff22cd006d', class: "bs-stepper-step__top" }, h("span", { key: '0d0e5ef0199013cd0ed59b26fe169c2bac727920', part: "icon", class: `bs-stepper-step__icon bs-stepper-step__icon--${state}` }, state === 'current' && h("span", { key: '6c9ab59838f63958a54b852c4a8f3babf1ddc48e', class: "bs-stepper-step__icon-dot" }), state === 'done' && h(CheckIcon, { key: '284d95f3e0ae3033ab2baa079c00cb2dc1fbcd3f' }), state === 'error' && h(ExclamationIcon, { key: '45b894a13cf54b0d5cb027d89646c712584bf6bb' })), h("span", { key: '79621665f9aecb82c4135ac2e1139ce1ee9af6be', part: "connector", class: "bs-stepper-step__connector", "aria-hidden": "true" }, h("span", { key: '91454b553634d90734dbd2b8bb9ab53d9bead49c', class: "bs-stepper-step__connector-line" }))), h("span", { key: 'd5f1606511df9745571d96cad36d38774fd943b4', part: "label", class: `bs-stepper-step__label bs-stepper-step__label--${state}` }, h("span", { key: '81df6e0b9607c22cfdad7e37095d245ec428e0f1', part: "name", class: "bs-stepper-step__name" }, this.name), this.showDescription && this.description && (h("span", { key: '77c4951d570219071e8acedc361a73eed6a89612', part: "description", class: "bs-stepper-step__description" }, this.description)))));
61
61
  }
62
62
  static get is() { return "bs-stepper-step"; }
63
63
  static get encapsulation() { return "shadow"; }
@@ -1,4 +1,4 @@
1
- import { html, nothing } from "lit";
1
+ import { html } from "lit";
2
2
  import { componentDescription, propDescription } from "../../../stories-utils";
3
3
  const iconPositions = ['start', 'top'];
4
4
  // `start`/`top` are the real prop values, but "Horizontal"/"Vertical" is what the layout actually
@@ -19,18 +19,18 @@ const meta = {
19
19
  // it can strip the element's own role. A plain block-level div has no visual effect worth
20
20
  // avoiding here (each story below is a single isolated demo, not part of a larger page layout).
21
21
  //
22
- // aria-label uses lit's `nothing` sentinel below, not null/'' -- a plain (non-boolean) lit
23
- // attribute binding renders null/'' as aria-label="" (present but empty), not an absent
24
- // attribute. axe-core then treats the light-DOM <bs-tab> host as ARIA-meaningful in its own
25
- // right and stops looking inside its shadow root for the real role="tab", which surfaces as an
26
- // aria-required-children violation on the *parent* tablist -- a very confusing place to look.
22
+ // ariaLabel is bound as a property (`.ariaLabel`), not an `aria-label` attribute. Any aria-label
23
+ // attribute on the light-DOM <bs-tab> host (even an empty one) makes axe-core treat the host as
24
+ // ARIA-meaningful in its own right and stop looking inside its shadow root for the real
25
+ // role="tab", which surfaces as an aria-required-children violation on the *parent* tablist -- a
26
+ // very confusing place to look. A property reaches the inner button without touching the host.
27
27
  render: args => html `
28
28
  <div role="tablist">
29
29
  <bs-tab
30
30
  ?selected=${args.selected}
31
31
  ?disabled=${args.disabled}
32
32
  icon-position=${args.iconPosition}
33
- aria-label=${args.ariaLabel || nothing}
33
+ .ariaLabel=${args.ariaLabel || null}
34
34
  >
35
35
  ${args.icon ? homeIcon : ''}${args.label}
36
36
  ${args.badge ? html `<bs-badge slot="badge" variant="primary">3</bs-badge>` : ''}
@@ -137,13 +137,8 @@ describe('bs-toolbar', () => {
137
137
  it('has no axe violations with overflow enabled', async () => {
138
138
  const { root } = await render(h("div", { style: { width: '200px' } }, h("bs-toolbar", { label: "Actions", overflow: true }, ['One', 'Two', 'Three', 'Four'].map(t => (h("bs-button", { slot: "left", variant: "subtle" }, t))))));
139
139
  await tick(200);
140
- // Known gap in the composed bs-popover (not this component): it puts aria-haspopup/aria-expanded
141
- // on a role-less wrapper div, which axe flags as aria-allowed-attr. Everything else must be clean.
142
140
  const results = await axe.run(root);
143
- const violations = results.violations
144
- .map(v => ({ ...v, nodes: v.nodes.filter(n => !(v.id === 'aria-allowed-attr' && n.html.includes('bs-popover__trigger'))) }))
145
- .filter(v => v.nodes.length > 0)
146
- .map(v => `${v.id}: ${v.help}`);
141
+ const violations = results.violations.map(v => `${v.id}: ${v.help}`);
147
142
  expect(violations).toEqual([]);
148
143
  });
149
144
  });
@@ -1,10 +1,12 @@
1
1
  import { html } from "lit";
2
2
  import { componentDescription } from "../../stories-utils";
3
+ // A bs-tree-item is role="treeitem", which must sit inside a role="tree" -- a bare `div` stands in for
4
+ // bs-tree here so the item is shown on its own without the tree's roving-tabindex behaviour.
3
5
  const meta = {
4
6
  title: 'Components/bs-tree-item',
5
7
  parameters: { docs: { description: { component: componentDescription('bs-tree-item') } } },
6
8
  render: args => html `
7
- <div style="width: 360px">
9
+ <div role="tree" aria-label="Example tree" style="width: 360px">
8
10
  <bs-tree-item label=${args.label} ?expanded=${args.expanded} ?selected=${args.selected} ?disabled=${args.disabled} .showIcon=${args.showIcon}>
9
11
  <bs-tree-item label="Child item"></bs-tree-item>
10
12
  </bs-tree-item>
@@ -27,12 +29,12 @@ export const Disabled = { args: { disabled: true } };
27
29
  export const WithoutIcon = { args: { showIcon: false } };
28
30
  export const Child = {
29
31
  render: () => html `
30
- <div style="width: 360px"><bs-tree-item label="Leaf item"></bs-tree-item></div>
32
+ <div role="tree" aria-label="Example tree" style="width: 360px"><bs-tree-item label="Leaf item"></bs-tree-item></div>
31
33
  `,
32
34
  };
33
35
  export const Levels = {
34
36
  render: () => html `
35
- <div style="width: 360px">
37
+ <div role="tree" aria-label="Example tree" style="width: 360px">
36
38
  <bs-tree-item label="Level 1" expanded>
37
39
  <bs-tree-item label="Level 2" expanded>
38
40
  <bs-tree-item label="Level 3"></bs-tree-item>
@@ -43,7 +45,7 @@ export const Levels = {
43
45
  };
44
46
  export const CustomIcon = {
45
47
  render: () => html `
46
- <div style="width: 360px">
48
+ <div role="tree" aria-label="Example tree" style="width: 360px">
47
49
  <bs-tree-item label="Starred">
48
50
  <svg slot="icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
49
51
  <path d="M12 3.75L14.6 9.1L20.5 9.9L16.2 14L17.3 19.9L12 17.1L6.7 19.9L7.8 14L3.5 9.9L9.4 9.1L12 3.75Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round" />