@sapudex/web-components 2.51.0 → 2.52.0
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/cypress/specs/CountrySelector.cy.tsx +21 -0
- package/cypress/specs/List.cy.tsx +119 -10
- package/cypress/specs/SelectBox.cy.tsx +414 -70
- package/dist/CountrySelector.js +1 -0
- package/dist/CountrySelector.js.map +1 -1
- package/dist/List.d.ts +10 -0
- package/dist/List.js +35 -0
- package/dist/List.js.map +1 -1
- package/dist/ListGroupHeaderBase.js +3 -0
- package/dist/ListGroupHeaderBase.js.map +1 -1
- package/dist/ListItem.d.ts +1 -0
- package/dist/ListItem.js +11 -3
- package/dist/ListItem.js.map +1 -1
- package/dist/ListItemBase.d.ts +9 -0
- package/dist/ListItemBase.js +12 -0
- package/dist/ListItemBase.js.map +1 -1
- package/dist/ListSelectAll.js +1 -1
- package/dist/ListSelectAll.js.map +1 -1
- package/dist/SearchableListBase.d.ts +10 -0
- package/dist/SearchableListBase.js +29 -6
- package/dist/SearchableListBase.js.map +1 -1
- package/dist/SearchableListBaseTemplate.js +32 -5
- package/dist/SearchableListBaseTemplate.js.map +1 -1
- package/dist/SelectBase.d.ts +65 -1
- package/dist/SelectBase.js +446 -59
- package/dist/SelectBase.js.map +1 -1
- package/dist/SelectBox.js +24 -2
- package/dist/SelectBox.js.map +1 -1
- package/dist/SelectBoxTemplate.js +2 -2
- package/dist/SelectBoxTemplate.js.map +1 -1
- package/dist/assets/{InputSuggestions-7eP5S95I.js → InputSuggestions-0wafO-g3.js} +1 -1
- package/dist/assets/{ListItemStandardExpandableTextTemplate-CLcWBFcg.js → ListItemStandardExpandableTextTemplate-DoOYtqI8.js} +1 -1
- package/dist/assets/{bundle.esm-D4--IDh7.js → bundle.esm-DwHZ3nCW.js} +241 -241
- package/dist/assets/messagebundle_nl-BKUpVV-D.js +1 -0
- package/dist/css/themes/AccordionItem.css +1 -1
- package/dist/css/themes/Divider.css +1 -1
- package/dist/css/themes/Link.css +1 -1
- package/dist/css/themes/ListGroupHeader.css +1 -1
- package/dist/css/themes/ListItem.css +1 -1
- package/dist/css/themes/SelectBase.css +1 -1
- package/dist/css/themes/SelectBox.css +1 -1
- package/dist/generated/assets/i18n/messagebundle_nl.json +1 -1
- package/dist/generated/templates/ListTemplate.lit.js +1 -1
- package/dist/generated/templates/ListTemplate.lit.js.map +1 -1
- package/dist/generated/themes/AccordionItem.css.d.ts +1 -1
- package/dist/generated/themes/AccordionItem.css.js +1 -1
- package/dist/generated/themes/AccordionItem.css.js.map +1 -1
- package/dist/generated/themes/Divider.css.d.ts +1 -1
- package/dist/generated/themes/Divider.css.js +1 -1
- package/dist/generated/themes/Divider.css.js.map +1 -1
- package/dist/generated/themes/Link.css.d.ts +1 -1
- package/dist/generated/themes/Link.css.js +1 -1
- package/dist/generated/themes/Link.css.js.map +1 -1
- package/dist/generated/themes/ListGroupHeader.css.d.ts +1 -1
- package/dist/generated/themes/ListGroupHeader.css.js +1 -1
- package/dist/generated/themes/ListGroupHeader.css.js.map +1 -1
- package/dist/generated/themes/ListItem.css.d.ts +1 -1
- package/dist/generated/themes/ListItem.css.js +1 -1
- package/dist/generated/themes/ListItem.css.js.map +1 -1
- package/dist/generated/themes/SelectBase.css.d.ts +1 -1
- package/dist/generated/themes/SelectBase.css.js +1 -1
- package/dist/generated/themes/SelectBase.css.js.map +1 -1
- package/dist/generated/themes/SelectBox.css.d.ts +1 -1
- package/dist/generated/themes/SelectBox.css.js +1 -1
- package/dist/generated/themes/SelectBox.css.js.map +1 -1
- package/dist/messagebundle_nl.properties +126 -0
- package/dist/web-types.json +1 -1
- package/package.json +1 -1
- package/src/CountrySelector.ts +1 -0
- package/src/List.hbs +2 -1
- package/src/List.ts +36 -0
- package/src/ListGroupHeaderBase.ts +3 -0
- package/src/ListItem.ts +12 -3
- package/src/ListItemBase.ts +11 -0
- package/src/ListSelectAll.ts +1 -1
- package/src/SearchableListBase.ts +31 -8
- package/src/SearchableListBaseTemplate.tsx +38 -6
- package/src/SelectBase.ts +466 -69
- package/src/SelectBox.ts +27 -2
- package/src/SelectBoxTemplate.tsx +4 -3
- package/src/generated/templates/ListTemplate.lit.ts +1 -1
- package/src/generated/themes/AccordionItem.css.ts +1 -1
- package/src/generated/themes/Divider.css.ts +1 -1
- package/src/generated/themes/Link.css.ts +1 -1
- package/src/generated/themes/ListGroupHeader.css.ts +1 -1
- package/src/generated/themes/ListItem.css.ts +1 -1
- package/src/generated/themes/SelectBase.css.ts +1 -1
- package/src/generated/themes/SelectBox.css.ts +1 -1
- package/src/i18n/messagebundle_nl.properties +126 -0
- package/src/themes/AccordionItem.css +8 -3
- package/src/themes/Divider.css +2 -0
- package/src/themes/Link.css +20 -2
- package/src/themes/ListGroupHeader.css +18 -1
- package/src/themes/ListItem.css +2 -1
- package/src/themes/SelectBase.css +2 -1
- package/src/themes/SelectBox.css +10 -0
- package/dist/assets/messagebundle_nl-etaldyfo.js +0 -1
package/src/ListItem.ts
CHANGED
|
@@ -366,11 +366,20 @@ class UDExListItem extends UDExListItemBase {
|
|
|
366
366
|
}
|
|
367
367
|
|
|
368
368
|
get roleItem(): string {
|
|
369
|
+
// When the host element carries the role (aria-activedescendant mode), the
|
|
370
|
+
// inner [part=item] div must be presentational to avoid a duplicate ARIA node.
|
|
371
|
+
if (this._useHostAriaRole) {
|
|
372
|
+
return "presentation";
|
|
373
|
+
}
|
|
374
|
+
return this.accessibleRole.length > 0 ? this.accessibleRole : listItemRole;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
get effectiveRole(): string {
|
|
369
378
|
return this.accessibleRole.length > 0 ? this.accessibleRole : listItemRole;
|
|
370
379
|
}
|
|
371
380
|
|
|
372
381
|
get ariaCheckedValue(): "true" | "false" | "mixed" | undefined {
|
|
373
|
-
if (this.
|
|
382
|
+
if (this.effectiveRole !== "treeitem") {
|
|
374
383
|
return undefined;
|
|
375
384
|
}
|
|
376
385
|
// Only MultiSelect treeitems expose aria-checked; SingleSelect uses aria-selected.
|
|
@@ -384,7 +393,7 @@ class UDExListItem extends UDExListItemBase {
|
|
|
384
393
|
}
|
|
385
394
|
|
|
386
395
|
get ariaSelectedValue(): "true" | "false" | undefined {
|
|
387
|
-
if (this.
|
|
396
|
+
if (this.effectiveRole !== "treeitem") {
|
|
388
397
|
return undefined;
|
|
389
398
|
}
|
|
390
399
|
// Only SingleSelect treeitems expose aria-selected; MultiSelect uses aria-checked.
|
|
@@ -402,7 +411,7 @@ class UDExListItem extends UDExListItemBase {
|
|
|
402
411
|
}
|
|
403
412
|
|
|
404
413
|
get checkboxAriaHidden(): boolean {
|
|
405
|
-
return this.
|
|
414
|
+
return this.effectiveRole === "treeitem";
|
|
406
415
|
}
|
|
407
416
|
|
|
408
417
|
get _ariaDisabled(): boolean | undefined {
|
package/src/ListItemBase.ts
CHANGED
|
@@ -229,6 +229,17 @@ class UDExListItemBase extends UI5Element {
|
|
|
229
229
|
@property({ type: Boolean })
|
|
230
230
|
_filtered!: boolean;
|
|
231
231
|
|
|
232
|
+
/**
|
|
233
|
+
* When true, the host element carries role + ARIA state attributes directly
|
|
234
|
+
* (set by the outer template) so that aria-activedescendant works across shadow DOM.
|
|
235
|
+
* The inner [part=item] div is then rendered as role="presentation".
|
|
236
|
+
*
|
|
237
|
+
* @default false
|
|
238
|
+
* @private
|
|
239
|
+
*/
|
|
240
|
+
@property({ type: Boolean })
|
|
241
|
+
_useHostAriaRole: boolean = false;
|
|
242
|
+
|
|
232
243
|
/**
|
|
233
244
|
* Defines the tab index of the component.
|
|
234
245
|
*
|
package/src/ListSelectAll.ts
CHANGED
|
@@ -268,6 +268,7 @@ class UDExSearchableListBase extends UI5Element {
|
|
|
268
268
|
_shouldFilterItems?: boolean;
|
|
269
269
|
_accSearchResultInfo?: string;
|
|
270
270
|
_groupCollapseStateBeforeSearch: Map<string, boolean>;
|
|
271
|
+
_pendingHeightReset: boolean = false;
|
|
271
272
|
|
|
272
273
|
constructor() {
|
|
273
274
|
super();
|
|
@@ -282,6 +283,19 @@ class UDExSearchableListBase extends UI5Element {
|
|
|
282
283
|
}
|
|
283
284
|
}
|
|
284
285
|
|
|
286
|
+
onAfterRendering() {
|
|
287
|
+
if (this._pendingHeightReset) {
|
|
288
|
+
this._pendingHeightReset = false;
|
|
289
|
+
this.shadowRoot?.querySelectorAll<HTMLElement & { setContentHeight(): void }>("[udex-list-group-header]")
|
|
290
|
+
.forEach(header => {
|
|
291
|
+
if (header.hasAttribute("collapsible")) {
|
|
292
|
+
header.style.removeProperty("--udex-list-content-max-height");
|
|
293
|
+
header.setContentHeight();
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
285
299
|
onItemPress(e:CustomEvent) {
|
|
286
300
|
const dropdownItemId: string = this.getDataTokenId(e.detail.item as UDExListItemBase);
|
|
287
301
|
this.handleDropdownItemPress(dropdownItemId);
|
|
@@ -484,21 +498,19 @@ class UDExSearchableListBase extends UI5Element {
|
|
|
484
498
|
}
|
|
485
499
|
|
|
486
500
|
restoreGroupCollapseState(): void {
|
|
487
|
-
if (!this._groupCollapseStateBeforeSearch.size) {
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
501
|
this.items.forEach(item => {
|
|
492
502
|
if (item.isGroup) {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
503
|
+
if (this._groupCollapseStateBeforeSearch.size) {
|
|
504
|
+
const collapsed = this._groupCollapseStateBeforeSearch.get(item._id);
|
|
505
|
+
if (collapsed !== undefined) {
|
|
506
|
+
item.collapsed = collapsed;
|
|
507
|
+
}
|
|
497
508
|
}
|
|
498
509
|
}
|
|
499
510
|
});
|
|
500
511
|
|
|
501
512
|
this._groupCollapseStateBeforeSearch.clear();
|
|
513
|
+
this._pendingHeightReset = true;
|
|
502
514
|
}
|
|
503
515
|
|
|
504
516
|
getSlottedItems(items: Array<UDExListItemBase | UDExListGroupHeaderBase>): Array<UDExListItemBase | UDExListGroupHeaderBase> {
|
|
@@ -732,6 +744,17 @@ class UDExSearchableListBase extends UI5Element {
|
|
|
732
744
|
return false;
|
|
733
745
|
}
|
|
734
746
|
|
|
747
|
+
/**
|
|
748
|
+
* Whether items are navigated via aria-activedescendant (focus stays on the
|
|
749
|
+
* combobox input) rather than via DOM focus on each item. When true,
|
|
750
|
+
* SearchableListBaseTemplate places role + ARIA state attrs on the host
|
|
751
|
+
* element of each list-item so that screen readers can read them through the
|
|
752
|
+
* aria-activedescendant pointer.
|
|
753
|
+
*/
|
|
754
|
+
get _activeDescendantMode(): boolean {
|
|
755
|
+
return false;
|
|
756
|
+
}
|
|
757
|
+
|
|
735
758
|
get additionalClassName(): string {
|
|
736
759
|
return `udex-searchable-list__header`;
|
|
737
760
|
}
|
|
@@ -9,6 +9,7 @@ import UDExListItem from "./ListItem.js";
|
|
|
9
9
|
import UDExListGroupHeader from "./ListGroupHeader.js";
|
|
10
10
|
import UDExListSelectAll from "./ListSelectAll.js";
|
|
11
11
|
import type { AriaRole } from "@ui5/webcomponents-base/dist/types.js";
|
|
12
|
+
import { cleanLabel } from "./utils/listItemLabelUtils.js";
|
|
12
13
|
|
|
13
14
|
export default function SearchableListBaseTemplate(this: UDExSearchableListBase) {
|
|
14
15
|
return (
|
|
@@ -58,7 +59,7 @@ export default function SearchableListBaseTemplate(this: UDExSearchableListBase)
|
|
|
58
59
|
{this._filteredItems.map((processedItem, index) => (
|
|
59
60
|
processedItem.isGroup
|
|
60
61
|
? renderGroup.call(this, processedItem, index)
|
|
61
|
-
: renderListItem(processedItem.item as UDExListItem, index, "none", this.listItemRole, this.fireFirstIconClickEvent, this.fireSecondIconClickEvent)
|
|
62
|
+
: renderListItem(processedItem.item as UDExListItem, index, "none", this.listItemRole, this.fireFirstIconClickEvent, this.fireSecondIconClickEvent, this._activeDescendantMode, this.isTreeStructure, this.modeMultiSelect)
|
|
62
63
|
))}
|
|
63
64
|
</UDExList>
|
|
64
65
|
)}
|
|
@@ -89,7 +90,7 @@ function renderSelectAll(this: UDExSearchableListBase, processedItem: UDExProces
|
|
|
89
90
|
onFirstIconClick={this.fireFirstIconClickEvent}
|
|
90
91
|
onSecondIconClick={this.fireSecondIconClickEvent}>
|
|
91
92
|
{groupItems.map((child, i) =>
|
|
92
|
-
renderListItem(child as UDExListItem, i, this.listItemRole, undefined, this.fireFirstIconClickEvent, this.fireSecondIconClickEvent)
|
|
93
|
+
renderListItem(child as UDExListItem, i, this.listItemRole, undefined, this.fireFirstIconClickEvent, this.fireSecondIconClickEvent, this._activeDescendantMode, this.isTreeStructure, this.modeMultiSelect)
|
|
93
94
|
)}
|
|
94
95
|
</UDExListSelectAll>
|
|
95
96
|
);
|
|
@@ -99,10 +100,22 @@ function renderGroupHeader(this: UDExSearchableListBase, processedItem: UDExProc
|
|
|
99
100
|
const { item, groupItems } = processedItem;
|
|
100
101
|
const header = item as UDExListGroupHeaderBase;
|
|
101
102
|
const childAccessibleRole = this.isTreeStructure ? "treeitem" : undefined;
|
|
103
|
+
// In aria-activedescendant mode (SelectBox), each group header host element
|
|
104
|
+
// must carry role + ARIA state so screen readers can reach them via the pointer.
|
|
105
|
+
const hostAriaAttrs = this._activeDescendantMode ? {
|
|
106
|
+
role: (this.isTreeStructure ? "treeitem" : "option") as AriaRole,
|
|
107
|
+
"aria-label": cleanLabel(item.label ?? ""),
|
|
108
|
+
"aria-selected": (this.modeMultiSelect ? undefined : (item.selected ? "true" : "false")) as string | undefined,
|
|
109
|
+
"aria-checked": (this.modeMultiSelect ? (header.indeterminate ? "mixed" : (item.selected ? "true" : "false")) : undefined) as string | undefined,
|
|
110
|
+
"aria-disabled": item.disabled ? "true" as string : undefined,
|
|
111
|
+
"aria-expanded": header.collapsible ? (header.expanded ? "true" as string : "false" as string) : undefined,
|
|
112
|
+
"_use-host-aria-role": true,
|
|
113
|
+
} : { role: (this.isTreeStructure ? "none" : "group") as AriaRole };
|
|
102
114
|
return (
|
|
103
115
|
<UDExListGroupHeader
|
|
104
116
|
key={index}
|
|
105
|
-
|
|
117
|
+
id={item._id}
|
|
118
|
+
{...hostAriaAttrs}
|
|
106
119
|
_tree-mode={this.isTreeStructure || undefined}
|
|
107
120
|
{...(index === 0 ? { "_no-border": "" } : {})}
|
|
108
121
|
_additional-class-name="udex-searchable-list__header"
|
|
@@ -121,7 +134,7 @@ function renderGroupHeader(this: UDExSearchableListBase, processedItem: UDExProc
|
|
|
121
134
|
onSecondIconClick={this.fireSecondIconClickEvent}
|
|
122
135
|
indeterminate={header.indeterminate}>
|
|
123
136
|
{groupItems.map((child, i) =>
|
|
124
|
-
renderListItem(child as UDExListItem, i, "none", childAccessibleRole, this.fireFirstIconClickEvent, this.fireSecondIconClickEvent)
|
|
137
|
+
renderListItem(child as UDExListItem, i, "none", childAccessibleRole, this.fireFirstIconClickEvent, this.fireSecondIconClickEvent, this._activeDescendantMode, this.isTreeStructure, this.modeMultiSelect)
|
|
125
138
|
)}
|
|
126
139
|
</UDExListGroupHeader>
|
|
127
140
|
);
|
|
@@ -134,11 +147,30 @@ function renderListItem(
|
|
|
134
147
|
accessibleRole: string | undefined,
|
|
135
148
|
onFirstIconClick: (e: CustomEvent) => void,
|
|
136
149
|
onSecondIconClick: (e: CustomEvent) => void,
|
|
150
|
+
activeDescendantMode?: boolean,
|
|
151
|
+
isTreeStructure?: boolean,
|
|
152
|
+
modeMultiSelect?: boolean,
|
|
137
153
|
) {
|
|
154
|
+
// In aria-activedescendant mode (SelectBox), role + ARIA state must be on the
|
|
155
|
+
// host element so screen readers can reach them via the aria-activedescendant pointer.
|
|
156
|
+
const effectiveRole = accessibleRole ?? (role !== "none" ? role : undefined) ?? "option";
|
|
157
|
+
const hostRole = activeDescendantMode ? (effectiveRole as AriaRole) : (role as AriaRole);
|
|
158
|
+
const hostAriaAttrs = activeDescendantMode ? {
|
|
159
|
+
"aria-label": "test",
|
|
160
|
+
// option role (listbox): always aria-selected; treeitem + MultiSelect: aria-checked only
|
|
161
|
+
"aria-selected": (effectiveRole !== "treeitem" || !modeMultiSelect) ? (item.selected ? "true" : "false") as string : undefined,
|
|
162
|
+
"aria-checked": (effectiveRole === "treeitem" && !!modeMultiSelect) ? (item.indeterminate ? "mixed" : (item.selected ? "true" : "false")) as string : undefined,
|
|
163
|
+
"aria-disabled": item.disabled ? "true" as string : undefined,
|
|
164
|
+
"_use-host-aria-role": true,
|
|
165
|
+
...(accessibleRole ? { "accessible-role": accessibleRole } : {}),
|
|
166
|
+
} : {
|
|
167
|
+
...(accessibleRole ? { "accessible-role": accessibleRole } : {}),
|
|
168
|
+
};
|
|
138
169
|
return (
|
|
139
170
|
<UDExListItem
|
|
140
171
|
key={index}
|
|
141
|
-
|
|
172
|
+
id={item._id}
|
|
173
|
+
role={hostRole}
|
|
142
174
|
data-token-id={item._id}
|
|
143
175
|
controlled={item.controlled}
|
|
144
176
|
mode={item.mode}
|
|
@@ -164,7 +196,7 @@ function renderListItem(
|
|
|
164
196
|
flag-position={item.flagPosition}
|
|
165
197
|
size={item.size}
|
|
166
198
|
type={item.type}
|
|
167
|
-
{...
|
|
199
|
+
{...hostAriaAttrs}
|
|
168
200
|
{...((item as any).innerHTML ? { dangerouslySetInnerHTML: { __html: (item as any).innerHTML } } : {})}>
|
|
169
201
|
</UDExListItem>
|
|
170
202
|
);
|