@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.
Files changed (97) hide show
  1. package/cypress/specs/CountrySelector.cy.tsx +21 -0
  2. package/cypress/specs/List.cy.tsx +119 -10
  3. package/cypress/specs/SelectBox.cy.tsx +414 -70
  4. package/dist/CountrySelector.js +1 -0
  5. package/dist/CountrySelector.js.map +1 -1
  6. package/dist/List.d.ts +10 -0
  7. package/dist/List.js +35 -0
  8. package/dist/List.js.map +1 -1
  9. package/dist/ListGroupHeaderBase.js +3 -0
  10. package/dist/ListGroupHeaderBase.js.map +1 -1
  11. package/dist/ListItem.d.ts +1 -0
  12. package/dist/ListItem.js +11 -3
  13. package/dist/ListItem.js.map +1 -1
  14. package/dist/ListItemBase.d.ts +9 -0
  15. package/dist/ListItemBase.js +12 -0
  16. package/dist/ListItemBase.js.map +1 -1
  17. package/dist/ListSelectAll.js +1 -1
  18. package/dist/ListSelectAll.js.map +1 -1
  19. package/dist/SearchableListBase.d.ts +10 -0
  20. package/dist/SearchableListBase.js +29 -6
  21. package/dist/SearchableListBase.js.map +1 -1
  22. package/dist/SearchableListBaseTemplate.js +32 -5
  23. package/dist/SearchableListBaseTemplate.js.map +1 -1
  24. package/dist/SelectBase.d.ts +65 -1
  25. package/dist/SelectBase.js +446 -59
  26. package/dist/SelectBase.js.map +1 -1
  27. package/dist/SelectBox.js +24 -2
  28. package/dist/SelectBox.js.map +1 -1
  29. package/dist/SelectBoxTemplate.js +2 -2
  30. package/dist/SelectBoxTemplate.js.map +1 -1
  31. package/dist/assets/{InputSuggestions-7eP5S95I.js → InputSuggestions-0wafO-g3.js} +1 -1
  32. package/dist/assets/{ListItemStandardExpandableTextTemplate-CLcWBFcg.js → ListItemStandardExpandableTextTemplate-DoOYtqI8.js} +1 -1
  33. package/dist/assets/{bundle.esm-D4--IDh7.js → bundle.esm-DwHZ3nCW.js} +241 -241
  34. package/dist/assets/messagebundle_nl-BKUpVV-D.js +1 -0
  35. package/dist/css/themes/AccordionItem.css +1 -1
  36. package/dist/css/themes/Divider.css +1 -1
  37. package/dist/css/themes/Link.css +1 -1
  38. package/dist/css/themes/ListGroupHeader.css +1 -1
  39. package/dist/css/themes/ListItem.css +1 -1
  40. package/dist/css/themes/SelectBase.css +1 -1
  41. package/dist/css/themes/SelectBox.css +1 -1
  42. package/dist/generated/assets/i18n/messagebundle_nl.json +1 -1
  43. package/dist/generated/templates/ListTemplate.lit.js +1 -1
  44. package/dist/generated/templates/ListTemplate.lit.js.map +1 -1
  45. package/dist/generated/themes/AccordionItem.css.d.ts +1 -1
  46. package/dist/generated/themes/AccordionItem.css.js +1 -1
  47. package/dist/generated/themes/AccordionItem.css.js.map +1 -1
  48. package/dist/generated/themes/Divider.css.d.ts +1 -1
  49. package/dist/generated/themes/Divider.css.js +1 -1
  50. package/dist/generated/themes/Divider.css.js.map +1 -1
  51. package/dist/generated/themes/Link.css.d.ts +1 -1
  52. package/dist/generated/themes/Link.css.js +1 -1
  53. package/dist/generated/themes/Link.css.js.map +1 -1
  54. package/dist/generated/themes/ListGroupHeader.css.d.ts +1 -1
  55. package/dist/generated/themes/ListGroupHeader.css.js +1 -1
  56. package/dist/generated/themes/ListGroupHeader.css.js.map +1 -1
  57. package/dist/generated/themes/ListItem.css.d.ts +1 -1
  58. package/dist/generated/themes/ListItem.css.js +1 -1
  59. package/dist/generated/themes/ListItem.css.js.map +1 -1
  60. package/dist/generated/themes/SelectBase.css.d.ts +1 -1
  61. package/dist/generated/themes/SelectBase.css.js +1 -1
  62. package/dist/generated/themes/SelectBase.css.js.map +1 -1
  63. package/dist/generated/themes/SelectBox.css.d.ts +1 -1
  64. package/dist/generated/themes/SelectBox.css.js +1 -1
  65. package/dist/generated/themes/SelectBox.css.js.map +1 -1
  66. package/dist/messagebundle_nl.properties +126 -0
  67. package/dist/web-types.json +1 -1
  68. package/package.json +1 -1
  69. package/src/CountrySelector.ts +1 -0
  70. package/src/List.hbs +2 -1
  71. package/src/List.ts +36 -0
  72. package/src/ListGroupHeaderBase.ts +3 -0
  73. package/src/ListItem.ts +12 -3
  74. package/src/ListItemBase.ts +11 -0
  75. package/src/ListSelectAll.ts +1 -1
  76. package/src/SearchableListBase.ts +31 -8
  77. package/src/SearchableListBaseTemplate.tsx +38 -6
  78. package/src/SelectBase.ts +466 -69
  79. package/src/SelectBox.ts +27 -2
  80. package/src/SelectBoxTemplate.tsx +4 -3
  81. package/src/generated/templates/ListTemplate.lit.ts +1 -1
  82. package/src/generated/themes/AccordionItem.css.ts +1 -1
  83. package/src/generated/themes/Divider.css.ts +1 -1
  84. package/src/generated/themes/Link.css.ts +1 -1
  85. package/src/generated/themes/ListGroupHeader.css.ts +1 -1
  86. package/src/generated/themes/ListItem.css.ts +1 -1
  87. package/src/generated/themes/SelectBase.css.ts +1 -1
  88. package/src/generated/themes/SelectBox.css.ts +1 -1
  89. package/src/i18n/messagebundle_nl.properties +126 -0
  90. package/src/themes/AccordionItem.css +8 -3
  91. package/src/themes/Divider.css +2 -0
  92. package/src/themes/Link.css +20 -2
  93. package/src/themes/ListGroupHeader.css +18 -1
  94. package/src/themes/ListItem.css +2 -1
  95. package/src/themes/SelectBase.css +2 -1
  96. package/src/themes/SelectBox.css +10 -0
  97. package/dist/assets/messagebundle_nl-etaldyfo.js +0 -1
@@ -235,4 +235,25 @@ describe("CountrySelector component", () => {
235
235
  cy.get("[udex-country-selector]").shadow().find("[udex-list-item]").first().click();
236
236
  cy.get("@selectionHandler").should("have.been.called");
237
237
  });
238
+
239
+ // Added: covers aria-required wiring on the combobox input in SelectBoxTemplate
240
+ // (shared template). The `required` boolean (inherited from SelectBase) drives
241
+ // aria-required={this.required} on the inner input[role="combobox"].
242
+ describe("aria-required accessibility", () => {
243
+ it("does not set aria-required=\"true\" on the combobox input by default (required not set)", () => {
244
+ cy.mount(<UDExCountrySelector id="country-selector-required" label="Countries"></UDExCountrySelector>);
245
+
246
+ cy.get("#country-selector-required").shadow()
247
+ .find('input[role="combobox"]')
248
+ .should("not.have.attr", "aria-required", "true");
249
+ });
250
+
251
+ it("sets aria-required=\"true\" on the combobox input when required is set", () => {
252
+ cy.mount(<UDExCountrySelector id="country-selector-required" label="Countries" required></UDExCountrySelector>);
253
+
254
+ cy.get("#country-selector-required").shadow()
255
+ .find('input[role="combobox"]')
256
+ .should("have.attr", "aria-required", "true");
257
+ });
258
+ });
238
259
  });
@@ -44,6 +44,16 @@ const collapsibleListRadioGroup = <UDExList id="list-group-header-collapsible" m
44
44
  </UDExListGroupHeader>
45
45
  </UDExList>;
46
46
 
47
+ // Regression fixture: grouped MultiSelect list WITHOUT any collapsible group header.
48
+ // This must stay on the listbox pattern (role="listbox", no aria-multiselectable, items
49
+ // role="option") because _hasCollapsibleGroups is false.
50
+ const nonCollapsibleList = <UDExList id="list-group-header-non-collapsible" mode="MultiSelect">
51
+ <UDExListGroupHeader id="group-header-non-collapsible" supporting-text="Supporting text" label="Group Header">
52
+ <UDExListItem>List Item1</UDExListItem>
53
+ <UDExListItem>List Item2</UDExListItem>
54
+ </UDExListGroupHeader>
55
+ </UDExList>;
56
+
47
57
  describe("List component - collapsible group header", () => {
48
58
  it("MultiSelect - collapse and expand the group when pressing arrow keys on grouping header", () => {
49
59
  cy.mount(collapsibleList);
@@ -171,12 +181,14 @@ describe("List component - collapsible group header", () => {
171
181
  });
172
182
 
173
183
  // Added: covers the fix for a collapsible SingleSelect group header being incorrectly
174
- // selectable via click/Enter. Because UDExList does not opt into the tree structure
175
- // (supportsTreeStructure stays false), a collapsible SingleSelect header here renders
176
- // the plain `single-group-header-collapsible` <div> with role="button" (non-tree branch
177
- // of singleCollapsibleHeaderRole). The <div> has no @click handler and only a
178
- // Left/Right arrow @keydown handler — so it can never be selected, only collapsed via
179
- // the arrows or the arrow <udex-button>.
184
+ // selectable via click/Enter. A collapsible SingleSelect header renders the plain
185
+ // `single-group-header-collapsible` <div> (NOT a udex-list-item). The <div> has no
186
+ // @click handler and only a Left/Right arrow @keydown handler — so it can never be
187
+ // selected, only collapsed via the arrows or the arrow <udex-button>.
188
+ // Updated: standalone UDExList now enters the WAI-ARIA tree pattern whenever it has a
189
+ // collapsible group header (via its own `_hasCollapsibleGroups` getter, which drives
190
+ // `_treeMode=true` on the group headers). So the header div's `singleCollapsibleHeaderRole`
191
+ // resolves to "treeitem" (was "button") — it is a non-selectable parent tree node.
180
192
  describe("List - SingleSelect collapsible group header is never selectable", () => {
181
193
  const headerDiv = () =>
182
194
  cy.get("#group-header-collapsible").shadow().find(".udex-list__group-content");
@@ -188,10 +200,14 @@ describe("List - SingleSelect collapsible group header is never selectable", ()
188
200
  cy.mount(collapsibleListSingleSelect);
189
201
  });
190
202
 
191
- it("renders the header as a plain div with role=\"button\" (non-tree mode)", () => {
203
+ // Updated: role is now "treeitem" (tree mode) instead of "button". A standalone
204
+ // collapsible list activates the tree pattern, so singleCollapsibleHeaderRole="treeitem".
205
+ // The header still renders as a plain div (no udex-list-item), so it is a non-selectable
206
+ // parent node with no aria-selected/aria-checked.
207
+ it("renders the header as a plain div with role=\"treeitem\" (tree mode)", () => {
192
208
  headerDiv()
193
209
  .should("exist")
194
- .and("have.attr", "role", "button")
210
+ .and("have.attr", "role", "treeitem")
195
211
  .and("have.attr", "tabindex", "0");
196
212
  cy.get("#group-header-collapsible").shadow()
197
213
  .find("udex-list-item").should("not.exist");
@@ -201,8 +217,9 @@ describe("List - SingleSelect collapsible group header is never selectable", ()
201
217
  // freshly-queried `.should()` calls. The doubled `.and("not.have.attr", ...)` after a
202
218
  // shadow `.find()` re-yielded the subject as undefined (chai-jQuery "attr on undefined").
203
219
  // The standalone-List SingleSelect collapsible header renders the plain
204
- // `single-group-header-collapsible` <div> (.udex-list__group-content) with role="button"
205
- // (non-tree branch), aria-expanded, and no selection-state attribute.
220
+ // `single-group-header-collapsible` <div> (.udex-list__group-content) with role="treeitem"
221
+ // (tree mode), aria-expanded, and no selection-state attribute (parent tree nodes are not
222
+ // themselves selectable — only leaf items carry aria-selected).
206
223
  it("exposes aria-expanded but no selection-state attribute", () => {
207
224
  headerDiv().should("have.attr", "aria-expanded", "true");
208
225
  headerDiv().should("not.have.attr", "aria-selected");
@@ -276,6 +293,98 @@ describe("List - SingleSelect collapsible group header is never selectable", ()
276
293
  });
277
294
  });
278
295
 
296
+ // Added: covers the WAI-ARIA tree pattern that the standalone <udex-list> activates
297
+ // automatically whenever it has at least one collapsible <udex-list-group-header>.
298
+ // The trigger is UDExList's own `_hasCollapsibleGroups` getter (independent of
299
+ // SearchableListBase.supportsTreeStructure), which sets `_treeMode=true` on the group
300
+ // headers and `accessibleRole="treeitem"` on the leaf items. In that mode the inner
301
+ // `#udex-list` div gets role="tree" (aria-multiselectable="true" only in MultiSelect).
302
+ describe("List - collapsible group header uses tree pattern", () => {
303
+ const innerList = () =>
304
+ cy.get("[udex-list]").shadow().find("#udex-list");
305
+
306
+ it("MultiSelect: inner #udex-list div has role=\"tree\"", () => {
307
+ cy.mount(collapsibleList);
308
+ innerList().should("have.attr", "role", "tree");
309
+ });
310
+
311
+ it("SingleSelect: inner #udex-list div has role=\"tree\"", () => {
312
+ cy.mount(collapsibleListSingleSelect);
313
+ innerList().should("have.attr", "role", "tree");
314
+ });
315
+
316
+ it("MultiSelect collapsible: inner #udex-list has aria-multiselectable=\"true\"", () => {
317
+ cy.mount(collapsibleList);
318
+ innerList().should("have.attr", "aria-multiselectable", "true");
319
+ });
320
+
321
+ it("SingleSelect collapsible: inner #udex-list does NOT have aria-multiselectable", () => {
322
+ cy.mount(collapsibleListSingleSelect);
323
+ innerList().should("not.have.attr", "aria-multiselectable");
324
+ });
325
+
326
+ it("group header host gets the _tree-mode attribute", () => {
327
+ cy.mount(collapsibleList);
328
+ cy.get("#group-header-collapsible").should("have.attr", "_tree-mode");
329
+ });
330
+
331
+ it("MultiSelect collapsible header: inner udex-list-item exposes accessible-role=\"treeitem\"", () => {
332
+ cy.mount(collapsibleList);
333
+ // MultiSelect => useListItemHeader=true => the header renders a <udex-list-item>.
334
+ cy.get("#group-header-collapsible").shadow()
335
+ .find("udex-list-item").should("have.attr", "accessible-role", "treeitem");
336
+ });
337
+
338
+ it("MultiSelect collapsible header: shadow [part=item] has role=\"treeitem\" and aria-expanded", () => {
339
+ cy.mount(collapsibleList);
340
+ cy.get("#group-header-collapsible").shadow()
341
+ .find("udex-list-item").shadow()
342
+ .find("[part=item]")
343
+ .should("have.attr", "role", "treeitem")
344
+ .and("have.attr", "aria-expanded", "true");
345
+ });
346
+
347
+ it("SingleSelect collapsible header: plain div gets role=\"treeitem\"", () => {
348
+ cy.mount(collapsibleListSingleSelect);
349
+ // SingleSelect => useListItemHeader=false => plain .udex-list__group-content <div>,
350
+ // whose role comes from singleCollapsibleHeaderRole => "treeitem" in tree mode.
351
+ cy.get("#group-header-collapsible").shadow()
352
+ .find(".udex-list__group-content").should("have.attr", "role", "treeitem");
353
+ });
354
+
355
+ it("child items expose accessible-role=\"treeitem\" on the host", () => {
356
+ cy.mount(collapsibleList);
357
+ cy.get("#group-header-collapsible").find("udex-list-item")
358
+ .each($item => {
359
+ cy.wrap($item).should("have.attr", "accessible-role", "treeitem");
360
+ });
361
+ });
362
+
363
+ it("child items render role=\"treeitem\" on their shadow [part=item]", () => {
364
+ cy.mount(collapsibleList);
365
+ cy.get("#group-header-collapsible").find("udex-list-item").eq(0).shadow()
366
+ .find("[part=item]").should("have.attr", "role", "treeitem");
367
+ });
368
+
369
+ // Regression: a grouped list WITHOUT any collapsible header must NOT enter tree mode.
370
+ it("non-collapsible grouped list keeps role=\"listbox\" (no tree pattern)", () => {
371
+ cy.mount(nonCollapsibleList);
372
+ innerList().should("have.attr", "role", "listbox");
373
+ });
374
+
375
+ it("non-collapsible grouped list has no aria-multiselectable and no _tree-mode", () => {
376
+ cy.mount(nonCollapsibleList);
377
+ innerList().should("not.have.attr", "aria-multiselectable");
378
+ cy.get("#group-header-non-collapsible").should("not.have.attr", "_tree-mode");
379
+ });
380
+
381
+ it("non-collapsible list: child items render role=\"option\" on shadow [part=item]", () => {
382
+ cy.mount(nonCollapsibleList);
383
+ cy.get("#group-header-non-collapsible").find("udex-list-item").eq(0).shadow()
384
+ .find("[part=item]").should("have.attr", "role", "option");
385
+ });
386
+ });
387
+
279
388
  describe("List component - SingleContent accessible name", () => {
280
389
  it("uses accessible-name as aria-label when only accessible-name is set", () => {
281
390
  cy.mount(