@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.
- package/CHANGELOG.md +8 -0
- package/REFERENCE.md +2 -2
- package/custom-elements.json +2 -2
- package/dist/brandsync-wc/brandsync-wc.esm.js +1 -1
- package/dist/brandsync-wc/p-14719554.entry.js +1 -0
- package/dist/brandsync-wc/p-39c50762.entry.js +1 -0
- package/dist/brandsync-wc/p-60636c6c.entry.js +1 -0
- package/dist/brandsync-wc/p-6ae75ed0.entry.js +1 -0
- package/dist/brandsync-wc/p-6b1ba5ec.entry.js +1 -0
- package/dist/brandsync-wc/p-aa99b1f8.entry.js +1 -0
- package/dist/brandsync-wc/p-b6dc9234.entry.js +1 -0
- package/dist/brandsync-wc/p-c0e5db3d.entry.js +1 -0
- package/dist/brandsync-wc/{p-16f15ba5.entry.js → p-dc59a735.entry.js} +1 -1
- package/dist/cjs/bs-accordion.cjs.entry.js +1 -1
- package/dist/cjs/bs-carousel.cjs.entry.js +1 -1
- package/dist/cjs/bs-chip-informative.cjs.entry.js +1 -1
- package/dist/cjs/bs-company-switcher.cjs.entry.js +2 -2
- package/dist/cjs/bs-popover.cjs.entry.js +25 -1
- package/dist/cjs/bs-search-item.cjs.entry.js +1 -1
- package/dist/cjs/bs-shortcut-row.cjs.entry.js +1 -1
- package/dist/cjs/bs-slider.cjs.entry.js +1 -1
- package/dist/cjs/bs-stepper-step.cjs.entry.js +1 -1
- package/dist/collection/components/bs-accordion/bs-accordion.js +1 -1
- package/dist/collection/components/bs-carousel/bs-carousel.js +2 -2
- package/dist/collection/components/bs-carousel/bs-carousel.stories.js +9 -5
- package/dist/collection/components/bs-chip/bs-chip-informative/bs-chip-informative.js +1 -1
- package/dist/collection/components/bs-company-switcher/bs-company-switcher.cmp.test.js +13 -7
- package/dist/collection/components/bs-company-switcher/bs-company-switcher.js +2 -2
- package/dist/collection/components/bs-composer/bs-composer.stories.js +1 -1
- package/dist/collection/components/bs-dropdown/bs-dropdown.cmp.test.js +1 -3
- package/dist/collection/components/bs-menu-item/bs-menu-item.stories.js +19 -15
- package/dist/collection/components/bs-popover/bs-popover.cmp.test.js +11 -4
- package/dist/collection/components/bs-popover/bs-popover.js +26 -2
- package/dist/collection/components/bs-search-item/bs-search-item.js +1 -1
- package/dist/collection/components/bs-shortcut-row/bs-shortcut-row.js +1 -1
- package/dist/collection/components/bs-slider/bs-slider.js +1 -1
- package/dist/collection/components/bs-stepper/bs-stepper-step/bs-stepper-step.js +1 -1
- package/dist/collection/components/bs-tab/bs-tab/bs-tab.stories.js +7 -7
- package/dist/collection/components/bs-toolbar/bs-toolbar.cmp.test.js +1 -6
- package/dist/collection/components/bs-tree-item/bs-tree-item.stories.js +6 -4
- package/dist/collection/components/disabled-aria.cmp.test.js +19 -0
- package/dist/components/bs-accordion.js +1 -1
- package/dist/components/bs-app-switcher.js +1 -1
- package/dist/components/bs-carousel.js +1 -1
- package/dist/components/bs-chip-informative.js +1 -1
- package/dist/components/bs-command-palette.js +1 -1
- package/dist/components/bs-company-switcher.js +1 -1
- package/dist/components/bs-cookie-consent.js +1 -1
- package/dist/components/bs-datepicker.js +1 -1
- package/dist/components/bs-dropdown.js +1 -1
- package/dist/components/bs-global-search.js +1 -1
- package/dist/components/bs-input.js +1 -1
- package/dist/components/bs-keyboard-shortcuts.js +1 -1
- package/dist/components/bs-mega-menu.js +1 -1
- package/dist/components/bs-menu-block.js +1 -1
- package/dist/components/bs-popover.js +1 -1
- package/dist/components/bs-qr-code.js +1 -1
- package/dist/components/bs-search-item.js +1 -1
- package/dist/components/bs-session-timeout.js +1 -1
- package/dist/components/bs-shortcut-row.js +1 -1
- package/dist/components/bs-slider.js +1 -1
- package/dist/components/bs-stepper-step.js +1 -1
- package/dist/components/bs-timepicker.js +1 -1
- package/dist/components/bs-toolbar.js +1 -1
- package/dist/components/bs-whats-new-entry.js +1 -1
- package/dist/components/bs-whats-new.js +1 -1
- package/dist/components/{p-D3XmFqCQ.js → p-B5yjDh8h.js} +1 -1
- package/dist/components/p-B81aicKA.js +1 -0
- package/dist/components/{p-DYpJMHAH.js → p-C3YETziz.js} +1 -1
- package/dist/components/{p-DztU3DwY.js → p-CKTbatgt.js} +1 -1
- package/dist/components/p-DUV80kt-.js +1 -0
- package/dist/components/p-_zxYDrzi.js +1 -0
- package/dist/esm/bs-accordion.entry.js +1 -1
- package/dist/esm/bs-carousel.entry.js +1 -1
- package/dist/esm/bs-chip-informative.entry.js +1 -1
- package/dist/esm/bs-company-switcher.entry.js +2 -2
- package/dist/esm/bs-popover.entry.js +25 -1
- package/dist/esm/bs-search-item.entry.js +1 -1
- package/dist/esm/bs-shortcut-row.entry.js +1 -1
- package/dist/esm/bs-slider.entry.js +1 -1
- package/dist/esm/bs-stepper-step.entry.js +1 -1
- package/dist/types/components/bs-carousel/bs-carousel.d.ts +1 -1
- package/dist/types/components/bs-popover/bs-popover.d.ts +9 -1
- package/dist/types/components/disabled-aria.cmp.test.d.ts +1 -0
- package/package.json +6 -1
- package/reference.json +2 -2
- package/dist/brandsync-wc/p-050270f5.entry.js +0 -1
- package/dist/brandsync-wc/p-0f864a4f.entry.js +0 -1
- package/dist/brandsync-wc/p-105c316e.entry.js +0 -1
- package/dist/brandsync-wc/p-7632f773.entry.js +0 -1
- package/dist/brandsync-wc/p-86fdbe8f.entry.js +0 -1
- package/dist/brandsync-wc/p-c9888d09.entry.js +0 -1
- package/dist/brandsync-wc/p-e6f57fae.entry.js +0 -1
- package/dist/brandsync-wc/p-ff14edb6.entry.js +0 -1
- package/dist/components/p-CXv8kx8w.js +0 -1
- package/dist/components/p-CePOtLtZ.js +0 -1
- 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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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: '
|
|
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
|
|
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: '
|
|
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: '
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
-
<
|
|
14
|
-
<
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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
|
|
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
|
-
//
|
|
23
|
-
// attribute
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
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
|
-
|
|
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" />
|