@sapudex/web-components 2.33.0 → 2.34.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 (83) hide show
  1. package/cypress/specs/FilterMenu.cy.tsx +53 -0
  2. package/cypress/specs/ListItem.cy.tsx +48 -0
  3. package/cypress/specs/MastheadMenu.cy.tsx +564 -0
  4. package/cypress/specs/MastheadMobileMenu.cy.tsx +573 -0
  5. package/cypress/specs/SelectBox.cy.tsx +63 -0
  6. package/dist/ListItem.d.ts +3 -0
  7. package/dist/ListItem.js +10 -1
  8. package/dist/ListItem.js.map +1 -1
  9. package/dist/ListItemBase.d.ts +29 -0
  10. package/dist/ListItemBase.js +22 -0
  11. package/dist/ListItemBase.js.map +1 -1
  12. package/dist/MastheadItem.d.ts +2 -1
  13. package/dist/MastheadItem.js.map +1 -1
  14. package/dist/MastheadMenu.d.ts +2 -1
  15. package/dist/MastheadMenu.js.map +1 -1
  16. package/dist/SearchableListBase.d.ts +3 -1
  17. package/dist/SearchableListBase.js +7 -1
  18. package/dist/SearchableListBase.js.map +1 -1
  19. package/dist/SelectBase.js +4 -2
  20. package/dist/SelectBase.js.map +1 -1
  21. package/dist/assets/{InputSuggestions-B_Dl2L6w.js → InputSuggestions-Bz7HZXUc.js} +1 -1
  22. package/dist/assets/{ListItemStandardExpandableTextTemplate-9Z-aZmJx.js → ListItemStandardExpandableTextTemplate-CjE7hAvu.js} +1 -1
  23. package/dist/assets/{bundle-DbTEs-Za.css → bundle-JYo3bV8v.css} +1 -1
  24. package/dist/assets/{bundle.esm-BlIxH9k1.js → bundle.esm-mAR7ytdB.js} +216 -216
  25. package/dist/css/themes/ListItem.css +1 -1
  26. package/dist/css/themes/MastheadMenu.css +1 -1
  27. package/dist/css/themes/MastheadMobileMenu.css +1 -1
  28. package/dist/css/themes/RatingIndicator.css +1 -1
  29. package/dist/css/themes/Slider.css +1 -1
  30. package/dist/custom-elements-internal.json +1113 -23
  31. package/dist/custom-elements.json +1114 -24
  32. package/dist/generated/templates/ListItemTemplate.lit.js +32 -28
  33. package/dist/generated/templates/ListItemTemplate.lit.js.map +1 -1
  34. package/dist/generated/templates/MastheadMenuTemplate.lit.js +8 -1
  35. package/dist/generated/templates/MastheadMenuTemplate.lit.js.map +1 -1
  36. package/dist/generated/templates/MastheadMobileMenuTemplate.lit.js +10 -4
  37. package/dist/generated/templates/MastheadMobileMenuTemplate.lit.js.map +1 -1
  38. package/dist/generated/templates/SearchableListBaseTemplate.lit.js +3 -3
  39. package/dist/generated/templates/SearchableListBaseTemplate.lit.js.map +1 -1
  40. package/dist/generated/templates/SelectBoxTemplate.lit.js +12 -10
  41. package/dist/generated/templates/SelectBoxTemplate.lit.js.map +1 -1
  42. package/dist/generated/themes/ListItem.css.d.ts +1 -1
  43. package/dist/generated/themes/ListItem.css.js +1 -1
  44. package/dist/generated/themes/ListItem.css.js.map +1 -1
  45. package/dist/generated/themes/MastheadMenu.css.d.ts +1 -1
  46. package/dist/generated/themes/MastheadMenu.css.js +1 -1
  47. package/dist/generated/themes/MastheadMenu.css.js.map +1 -1
  48. package/dist/generated/themes/MastheadMobileMenu.css.d.ts +1 -1
  49. package/dist/generated/themes/MastheadMobileMenu.css.js +1 -1
  50. package/dist/generated/themes/MastheadMobileMenu.css.js.map +1 -1
  51. package/dist/generated/themes/RatingIndicator.css.d.ts +1 -1
  52. package/dist/generated/themes/RatingIndicator.css.js +1 -1
  53. package/dist/generated/themes/RatingIndicator.css.js.map +1 -1
  54. package/dist/generated/themes/Slider.css.d.ts +1 -1
  55. package/dist/generated/themes/Slider.css.js +1 -1
  56. package/dist/generated/themes/Slider.css.js.map +1 -1
  57. package/dist/vscode.html-custom-data.json +220 -0
  58. package/dist/web-types.json +361 -1
  59. package/package.json +1 -1
  60. package/src/ListItem.hbs +26 -2
  61. package/src/ListItem.ts +12 -1
  62. package/src/ListItemBase.ts +40 -0
  63. package/src/MastheadItem.ts +2 -1
  64. package/src/MastheadMenu.hbs +35 -0
  65. package/src/MastheadMenu.ts +2 -1
  66. package/src/MastheadMobileMenu.hbs +40 -9
  67. package/src/SearchableListBase.hbs +12 -0
  68. package/src/SearchableListBase.ts +11 -2
  69. package/src/SelectBase.ts +4 -2
  70. package/src/SelectBox.hbs +13 -5
  71. package/src/generated/templates/ListItemTemplate.lit.ts +32 -28
  72. package/src/generated/templates/MastheadMenuTemplate.lit.ts +8 -1
  73. package/src/generated/templates/MastheadMobileMenuTemplate.lit.ts +10 -4
  74. package/src/generated/templates/SearchableListBaseTemplate.lit.ts +3 -3
  75. package/src/generated/templates/SelectBoxTemplate.lit.ts +12 -10
  76. package/src/generated/themes/ListItem.css.ts +1 -1
  77. package/src/generated/themes/MastheadMenu.css.ts +1 -1
  78. package/src/generated/themes/MastheadMobileMenu.css.ts +1 -1
  79. package/src/generated/themes/RatingIndicator.css.ts +1 -1
  80. package/src/generated/themes/Slider.css.ts +1 -1
  81. package/src/themes/ListItem.css +6 -4
  82. package/src/themes/MastheadMenu.css +45 -0
  83. package/src/themes/MastheadMobileMenu.css +45 -0
@@ -0,0 +1,573 @@
1
+ import { UDExMastheadMobileMenu } from "../support/component-imports.js";
2
+
3
+ // Fixture: category with 3rd-level sub-items
4
+ const categoryWithSubItems = {
5
+ category: "Cloud Products",
6
+ index: 0,
7
+ items: [
8
+ {
9
+ title: "Platform Services",
10
+ url: "",
11
+ externalLink: false,
12
+ subItems: [
13
+ {
14
+ title: "SAP BTP",
15
+ url: "https://example.com/btp",
16
+ externalLink: false,
17
+ },
18
+ {
19
+ title: "SAP Integration Suite",
20
+ url: "https://example.com/integration",
21
+ externalLink: false,
22
+ },
23
+ ],
24
+ },
25
+ {
26
+ title: "ERP Solutions",
27
+ url: "",
28
+ externalLink: false,
29
+ subItems: [
30
+ {
31
+ title: "SAP S/4HANA Cloud",
32
+ url: "https://example.com/s4hana",
33
+ externalLink: false,
34
+ },
35
+ ],
36
+ },
37
+ ],
38
+ };
39
+
40
+ // Fixture: category with only flat items (no subItems)
41
+ const categoryWithoutSubItems = {
42
+ category: "Services",
43
+ index: 0,
44
+ items: [
45
+ {
46
+ title: "Consulting",
47
+ url: "https://example.com/consulting",
48
+ externalLink: false,
49
+ },
50
+ {
51
+ title: "Training",
52
+ url: "https://example.com/training",
53
+ externalLink: false,
54
+ },
55
+ ],
56
+ };
57
+
58
+ // Fixture: mixed — one item with subItems, one without
59
+ const categoryMixed = {
60
+ category: "Solutions",
61
+ index: 0,
62
+ items: [
63
+ {
64
+ title: "Cloud Solutions",
65
+ url: "",
66
+ externalLink: false,
67
+ subItems: [
68
+ {
69
+ title: "SAP Analytics Cloud",
70
+ url: "https://example.com/analytics",
71
+ externalLink: false,
72
+ },
73
+ ],
74
+ },
75
+ {
76
+ title: "Direct Link Item",
77
+ url: "https://example.com/direct",
78
+ externalLink: false,
79
+ },
80
+ ],
81
+ };
82
+
83
+ // Fixture: sub-category parent items all have urls (linked sub-categories)
84
+ const categoryWithLinkedSubCategories = {
85
+ category: "AI Platform",
86
+ index: 0,
87
+ items: [
88
+ {
89
+ title: "Build",
90
+ url: "https://example.com/build",
91
+ externalLink: false,
92
+ subItems: [
93
+ {
94
+ title: "Develop",
95
+ url: "https://example.com/develop",
96
+ externalLink: false,
97
+ },
98
+ ],
99
+ },
100
+ {
101
+ title: "Deploy",
102
+ url: "https://example.com/deploy",
103
+ externalLink: false,
104
+ subItems: [
105
+ {
106
+ title: "Staging",
107
+ url: "https://example.com/staging",
108
+ externalLink: false,
109
+ },
110
+ {
111
+ title: "Production",
112
+ url: "https://example.com/production",
113
+ externalLink: false,
114
+ },
115
+ ],
116
+ },
117
+ ],
118
+ };
119
+
120
+ // Fixture: mixed linked and unlinked sub-category parents
121
+ const categoryMixedLinkedAndUnlinked = {
122
+ category: "Data Services",
123
+ index: 0,
124
+ items: [
125
+ {
126
+ title: "Integration",
127
+ url: "https://example.com/integration",
128
+ externalLink: false,
129
+ subItems: [
130
+ {
131
+ title: "API Management",
132
+ url: "https://example.com/api-mgmt",
133
+ externalLink: false,
134
+ },
135
+ ],
136
+ },
137
+ {
138
+ title: "Analytics",
139
+ url: "",
140
+ externalLink: false,
141
+ subItems: [
142
+ {
143
+ title: "Dashboards",
144
+ url: "https://example.com/dashboards",
145
+ externalLink: false,
146
+ },
147
+ ],
148
+ },
149
+ ],
150
+ };
151
+
152
+ /**
153
+ * Helper: mount the mobile menu and activate category-items view.
154
+ * Sets _showCategoryItems = true and _category to the provided fixture,
155
+ * which triggers the category-items inline partial template.
156
+ */
157
+ function mountWithCategoryItems(category: typeof categoryWithSubItems) {
158
+ cy.mount(<UDExMastheadMobileMenu id="mobile-menu" />);
159
+ cy.get("[udex-masthead-mobile-menu]").then(($el) => {
160
+ const el = $el[0] as any;
161
+ el._category = category;
162
+ el._showCategoryItems = true;
163
+ });
164
+ // Wait for re-render after property changes
165
+ cy.get("[udex-masthead-mobile-menu]").shadow().as("shadow");
166
+ }
167
+
168
+ describe("UDExMastheadMobileMenu -- 3rd-level navigation (sub-items)", () => {
169
+ describe("Rendering sub-categories (no url — span)", () => {
170
+ beforeEach(() => {
171
+ mountWithCategoryItems(categoryWithSubItems);
172
+ });
173
+
174
+ it("renders a .udex-masthead-mobile-menu__sub-category span for each item with subItems", () => {
175
+ cy.get("@shadow")
176
+ .find(".udex-masthead-mobile-menu__sub-category")
177
+ .should("have.length", 2);
178
+ });
179
+
180
+ it("renders sub-category as a <span> element when the item has no url", () => {
181
+ cy.get("@shadow")
182
+ .find(".udex-masthead-mobile-menu__sub-category")
183
+ .first()
184
+ .should("have.prop", "tagName", "SPAN");
185
+ });
186
+
187
+ it("displays the correct title text for each sub-category span", () => {
188
+ cy.get("@shadow")
189
+ .find(".udex-masthead-mobile-menu__sub-category")
190
+ .eq(0)
191
+ .should("have.text", "Platform Services");
192
+
193
+ cy.get("@shadow")
194
+ .find(".udex-masthead-mobile-menu__sub-category")
195
+ .eq(1)
196
+ .should("have.text", "ERP Solutions");
197
+ });
198
+ });
199
+
200
+ describe("Rendering sub-list and sub-items", () => {
201
+ beforeEach(() => {
202
+ mountWithCategoryItems(categoryWithSubItems);
203
+ });
204
+
205
+ it("renders a .udex-masthead-mobile-menu__sub-list for each item with subItems", () => {
206
+ cy.get("@shadow")
207
+ .find(".udex-masthead-mobile-menu__sub-list")
208
+ .should("have.length", 2);
209
+ });
210
+
211
+ it("renders udex-masthead-menu-item elements inside each sub-list with correct count", () => {
212
+ // First sub-list: "Platform Services" has 2 sub-items
213
+ cy.get("@shadow")
214
+ .find(".udex-masthead-mobile-menu__sub-list")
215
+ .eq(0)
216
+ .find("udex-masthead-menu-item")
217
+ .should("have.length", 2);
218
+
219
+ // Second sub-list: "ERP Solutions" has 1 sub-item
220
+ cy.get("@shadow")
221
+ .find(".udex-masthead-mobile-menu__sub-list")
222
+ .eq(1)
223
+ .find("udex-masthead-menu-item")
224
+ .should("have.length", 1);
225
+ });
226
+
227
+ it("renders the correct label attribute for each sub-item menu-item", () => {
228
+ cy.get("@shadow")
229
+ .find(".udex-masthead-mobile-menu__sub-list")
230
+ .eq(0)
231
+ .find("udex-masthead-menu-item")
232
+ .eq(0)
233
+ .should("have.attr", "label", "SAP BTP");
234
+
235
+ cy.get("@shadow")
236
+ .find(".udex-masthead-mobile-menu__sub-list")
237
+ .eq(0)
238
+ .find("udex-masthead-menu-item")
239
+ .eq(1)
240
+ .should("have.attr", "label", "SAP Integration Suite");
241
+ });
242
+
243
+ it("sets the correct url attribute on sub-item menu-items", () => {
244
+ cy.get("@shadow")
245
+ .find(".udex-masthead-mobile-menu__sub-list")
246
+ .eq(0)
247
+ .find("udex-masthead-menu-item")
248
+ .eq(0)
249
+ .should("have.attr", "url", "https://example.com/btp");
250
+
251
+ cy.get("@shadow")
252
+ .find(".udex-masthead-mobile-menu__sub-list")
253
+ .eq(1)
254
+ .find("udex-masthead-menu-item")
255
+ .eq(0)
256
+ .should("have.attr", "url", "https://example.com/s4hana");
257
+ });
258
+
259
+ it("sets third-level attribute on sub-item menu-items", () => {
260
+ cy.get("@shadow")
261
+ .find(".udex-masthead-mobile-menu__sub-list")
262
+ .eq(0)
263
+ .find("udex-masthead-menu-item")
264
+ .first()
265
+ .should("have.attr", "third-level");
266
+ });
267
+
268
+ // Updated: has-border removed from sub-item menu-items in template
269
+ it("does not set has-border attribute on sub-item menu-items", () => {
270
+ cy.get("@shadow")
271
+ .find(".udex-masthead-mobile-menu__sub-list")
272
+ .eq(0)
273
+ .find("udex-masthead-menu-item")
274
+ .first()
275
+ .should("not.have.attr", "has-border");
276
+ });
277
+ });
278
+
279
+ describe("Sub-list accessibility", () => {
280
+ beforeEach(() => {
281
+ mountWithCategoryItems(categoryWithSubItems);
282
+ });
283
+
284
+ it("sets aria-label on the sub-list matching the sub-category title", () => {
285
+ cy.get("@shadow")
286
+ .find(".udex-masthead-mobile-menu__sub-list")
287
+ .eq(0)
288
+ .should("have.attr", "aria-label", "Platform Services");
289
+
290
+ cy.get("@shadow")
291
+ .find(".udex-masthead-mobile-menu__sub-list")
292
+ .eq(1)
293
+ .should("have.attr", "aria-label", "ERP Solutions");
294
+ });
295
+
296
+ it("wraps sub-items in a <ul> element for correct list semantics", () => {
297
+ cy.get("@shadow")
298
+ .find(".udex-masthead-mobile-menu__sub-list")
299
+ .first()
300
+ .should("have.prop", "tagName", "UL");
301
+ });
302
+ });
303
+
304
+ describe("Regression: items without subItems still render directly", () => {
305
+ beforeEach(() => {
306
+ mountWithCategoryItems(categoryWithoutSubItems);
307
+ });
308
+
309
+ it("does not render any .udex-masthead-mobile-menu__sub-category when no items have subItems", () => {
310
+ cy.get("@shadow")
311
+ .find(".udex-masthead-mobile-menu__sub-category")
312
+ .should("not.exist");
313
+ });
314
+
315
+ it("does not render any .udex-masthead-mobile-menu__sub-list when no items have subItems", () => {
316
+ cy.get("@shadow")
317
+ .find(".udex-masthead-mobile-menu__sub-list")
318
+ .should("not.exist");
319
+ });
320
+
321
+ it("renders udex-masthead-menu-item elements directly for flat items", () => {
322
+ cy.get("@shadow")
323
+ .find(".udex-masthead-mobile-menu__list udex-masthead-menu-item[third-level]")
324
+ .should("have.length", 2);
325
+ });
326
+
327
+ it("renders the correct labels for flat items", () => {
328
+ cy.get("@shadow")
329
+ .find(".udex-masthead-mobile-menu__list udex-masthead-menu-item[third-level]")
330
+ .eq(0)
331
+ .should("have.attr", "label", "Consulting");
332
+
333
+ cy.get("@shadow")
334
+ .find(".udex-masthead-mobile-menu__list udex-masthead-menu-item[third-level]")
335
+ .eq(1)
336
+ .should("have.attr", "label", "Training");
337
+ });
338
+
339
+ it("renders the correct url for flat items", () => {
340
+ cy.get("@shadow")
341
+ .find(".udex-masthead-mobile-menu__list udex-masthead-menu-item[third-level]")
342
+ .eq(0)
343
+ .should("have.attr", "url", "https://example.com/consulting");
344
+ });
345
+ });
346
+
347
+ describe("Mixed content: items with and without subItems in the same category", () => {
348
+ beforeEach(() => {
349
+ mountWithCategoryItems(categoryMixed);
350
+ });
351
+
352
+ it("renders a sub-category span only for the item that has subItems", () => {
353
+ cy.get("@shadow")
354
+ .find(".udex-masthead-mobile-menu__sub-category")
355
+ .should("have.length", 1)
356
+ .and("have.text", "Cloud Solutions");
357
+ });
358
+
359
+ it("renders a sub-list with the correct sub-item for the grouped item", () => {
360
+ cy.get("@shadow")
361
+ .find(".udex-masthead-mobile-menu__sub-list")
362
+ .should("have.length", 1)
363
+ .find("udex-masthead-menu-item")
364
+ .should("have.length", 1)
365
+ .and("have.attr", "label", "SAP Analytics Cloud");
366
+ });
367
+
368
+ it("renders the flat item as a direct udex-masthead-menu-item outside any sub-list", () => {
369
+ // The direct link item sits at top level in the main list, not inside a sub-list
370
+ cy.get("@shadow")
371
+ .find(
372
+ ".udex-masthead-mobile-menu__list > li > udex-masthead-menu-item[third-level]"
373
+ )
374
+ .should("have.length", 1)
375
+ .and("have.attr", "label", "Direct Link Item")
376
+ .and("have.attr", "url", "https://example.com/direct");
377
+ });
378
+ });
379
+
380
+ // Updated: linked sub-categories (with url) now have the
381
+ // .udex-masthead-mobile-menu__sub-category class restored. CSS variable
382
+ // remapping is done via the `udex-masthead-menu-item.udex-masthead-mobile-menu__sub-category`
383
+ // rule (no padding/border — those only apply to the span variant).
384
+ describe("Rendering linked sub-categories (item has url + subItems)", () => {
385
+ beforeEach(() => {
386
+ mountWithCategoryItems(categoryWithLinkedSubCategories);
387
+ });
388
+
389
+ // Updated: linked sub-categories now carry the __sub-category class again
390
+ it("renders .udex-masthead-mobile-menu__sub-category for linked items (udex-masthead-menu-item, not span)", () => {
391
+ cy.get("@shadow")
392
+ .find("udex-masthead-menu-item.udex-masthead-mobile-menu__sub-category")
393
+ .should("have.length", 2);
394
+ });
395
+
396
+ it("renders linked sub-category as a udex-masthead-menu-item (not a span) when the item has a url", () => {
397
+ cy.get("@shadow")
398
+ .find("udex-masthead-menu-item.udex-masthead-mobile-menu__sub-category")
399
+ .first()
400
+ .should("have.prop", "tagName")
401
+ .then((tagName: string) => {
402
+ expect(tagName.toLowerCase()).to.include("udex-masthead-menu-item");
403
+ });
404
+ });
405
+
406
+ it("sets the correct label attribute on the linked sub-category", () => {
407
+ cy.get("@shadow")
408
+ .find("udex-masthead-menu-item.udex-masthead-mobile-menu__sub-category")
409
+ .eq(0)
410
+ .should("have.attr", "label", "Build");
411
+
412
+ cy.get("@shadow")
413
+ .find("udex-masthead-menu-item.udex-masthead-mobile-menu__sub-category")
414
+ .eq(1)
415
+ .should("have.attr", "label", "Deploy");
416
+ });
417
+
418
+ it("sets the correct url attribute on the linked sub-category", () => {
419
+ cy.get("@shadow")
420
+ .find("udex-masthead-menu-item.udex-masthead-mobile-menu__sub-category")
421
+ .eq(0)
422
+ .should("have.attr", "url", "https://example.com/build");
423
+
424
+ cy.get("@shadow")
425
+ .find("udex-masthead-menu-item.udex-masthead-mobile-menu__sub-category")
426
+ .eq(1)
427
+ .should("have.attr", "url", "https://example.com/deploy");
428
+ });
429
+
430
+ it("sets the third-level attribute on the linked sub-category", () => {
431
+ cy.get("@shadow")
432
+ .find("udex-masthead-menu-item.udex-masthead-mobile-menu__sub-category")
433
+ .first()
434
+ .should("have.attr", "third-level");
435
+ });
436
+
437
+ // Updated: linked sub-category has the __sub-category class but does NOT
438
+ // have has-border — padding/border styles only apply to the span variant
439
+ it("does not set has-border attribute on the linked sub-category", () => {
440
+ cy.get("@shadow")
441
+ .find("udex-masthead-menu-item.udex-masthead-mobile-menu__sub-category")
442
+ .first()
443
+ .should("not.have.attr", "has-border");
444
+ });
445
+
446
+ it("still renders the sub-list beneath the linked sub-category", () => {
447
+ cy.get("@shadow")
448
+ .find(".udex-masthead-mobile-menu__sub-list")
449
+ .should("have.length", 2);
450
+ });
451
+
452
+ it("renders the correct sub-items inside the sub-list of a linked sub-category", () => {
453
+ // First sub-list (under "Build") has 1 sub-item: "Develop"
454
+ cy.get("@shadow")
455
+ .find(".udex-masthead-mobile-menu__sub-list")
456
+ .eq(0)
457
+ .find("udex-masthead-menu-item")
458
+ .should("have.length", 1)
459
+ .and("have.attr", "label", "Develop");
460
+
461
+ // Second sub-list (under "Deploy") has 2 sub-items: "Staging", "Production"
462
+ cy.get("@shadow")
463
+ .find(".udex-masthead-mobile-menu__sub-list")
464
+ .eq(1)
465
+ .find("udex-masthead-menu-item")
466
+ .should("have.length", 2);
467
+
468
+ cy.get("@shadow")
469
+ .find(".udex-masthead-mobile-menu__sub-list")
470
+ .eq(1)
471
+ .find("udex-masthead-menu-item")
472
+ .eq(0)
473
+ .should("have.attr", "label", "Staging");
474
+
475
+ cy.get("@shadow")
476
+ .find(".udex-masthead-mobile-menu__sub-list")
477
+ .eq(1)
478
+ .find("udex-masthead-menu-item")
479
+ .eq(1)
480
+ .should("have.attr", "label", "Production");
481
+ });
482
+
483
+ it("preserves aria-label on the sub-list matching the linked sub-category title", () => {
484
+ cy.get("@shadow")
485
+ .find(".udex-masthead-mobile-menu__sub-list")
486
+ .eq(0)
487
+ .should("have.attr", "aria-label", "Build");
488
+
489
+ cy.get("@shadow")
490
+ .find(".udex-masthead-mobile-menu__sub-list")
491
+ .eq(1)
492
+ .should("have.attr", "aria-label", "Deploy");
493
+ });
494
+ });
495
+
496
+ describe("Mixed linked and unlinked sub-categories in the same category", () => {
497
+ beforeEach(() => {
498
+ mountWithCategoryItems(categoryMixedLinkedAndUnlinked);
499
+ });
500
+
501
+ // Updated: linked sub-category now has the __sub-category class restored;
502
+ // target it via udex-masthead-menu-item.udex-masthead-mobile-menu__sub-category
503
+ it("renders the linked sub-category as a udex-masthead-menu-item with __sub-category class", () => {
504
+ // "Integration" has a url — should be a menu-item with the class
505
+ cy.get("@shadow")
506
+ .find(
507
+ "udex-masthead-menu-item.udex-masthead-mobile-menu__sub-category"
508
+ )
509
+ .should("have.length", 1)
510
+ .and("have.attr", "label", "Integration")
511
+ .and("have.attr", "url", "https://example.com/integration");
512
+ });
513
+
514
+ it("renders the unlinked sub-category as a span", () => {
515
+ // "Analytics" has no url — should be a span
516
+ cy.get("@shadow")
517
+ .find("span.udex-masthead-mobile-menu__sub-category")
518
+ .should("have.length", 1)
519
+ .and("have.text", "Analytics");
520
+ });
521
+
522
+ // Updated: both linked and unlinked sub-categories now have __sub-category class
523
+ it("renders two .udex-masthead-mobile-menu__sub-category elements (one linked, one unlinked)", () => {
524
+ cy.get("@shadow")
525
+ .find(".udex-masthead-mobile-menu__sub-category")
526
+ .should("have.length", 2);
527
+ });
528
+
529
+ it("renders sub-lists for both linked and unlinked sub-categories", () => {
530
+ cy.get("@shadow")
531
+ .find(".udex-masthead-mobile-menu__sub-list")
532
+ .should("have.length", 2);
533
+ });
534
+
535
+ it("renders the linked sub-category's sub-item correctly", () => {
536
+ cy.get("@shadow")
537
+ .find(".udex-masthead-mobile-menu__sub-list")
538
+ .eq(0)
539
+ .find("udex-masthead-menu-item")
540
+ .should("have.length", 1)
541
+ .and("have.attr", "label", "API Management")
542
+ .and("have.attr", "url", "https://example.com/api-mgmt");
543
+ });
544
+
545
+ it("renders the unlinked sub-category's sub-item correctly", () => {
546
+ cy.get("@shadow")
547
+ .find(".udex-masthead-mobile-menu__sub-list")
548
+ .eq(1)
549
+ .find("udex-masthead-menu-item")
550
+ .should("have.length", 1)
551
+ .and("have.attr", "label", "Dashboards")
552
+ .and("have.attr", "url", "https://example.com/dashboards");
553
+ });
554
+
555
+ // Updated: linked sub-category has __sub-category class but NOT has-border;
556
+ // padding/border styles only apply to the span variant via CSS rule split
557
+ it("sets third-level but not has-border on the linked sub-category menu-item", () => {
558
+ cy.get("@shadow")
559
+ .find(
560
+ "udex-masthead-menu-item.udex-masthead-mobile-menu__sub-category"
561
+ )
562
+ .first()
563
+ .should("have.attr", "third-level");
564
+
565
+ cy.get("@shadow")
566
+ .find(
567
+ "udex-masthead-menu-item.udex-masthead-mobile-menu__sub-category"
568
+ )
569
+ .first()
570
+ .should("not.have.attr", "has-border");
571
+ });
572
+ });
573
+ });
@@ -1,6 +1,17 @@
1
1
  import { UDExSelectBox, UDExSelectBoxSelectAll, UDExSelectBoxGroupHeader, UDExSelectBoxItem, UDExButton } from "../support/component-imports.js";
2
2
  import { UDExListMode } from "../../src/types/List.js";
3
3
 
4
+ const selectBoxWithRating = <div style="width: 350px; position:relative;"><UDExSelectBox
5
+ id="select-box"
6
+ mode={UDExListMode.MultiSelect}
7
+ label="Rating">
8
+ <UDExSelectBoxItem rating="5" ratingCount="5.0" totalCount="23"></UDExSelectBoxItem>
9
+ <UDExSelectBoxItem rating="4" ratingCount="4.0" totalCount="17"></UDExSelectBoxItem>
10
+ <UDExSelectBoxItem rating="3" ratingCount="3.0" totalCount="34"></UDExSelectBoxItem>
11
+ <UDExSelectBoxItem rating="2" ratingCount="2.0" totalCount="40"></UDExSelectBoxItem>
12
+ <UDExSelectBoxItem rating="1" ratingCount="1.0" totalCount="73"></UDExSelectBoxItem>
13
+ </UDExSelectBox></div>
14
+
4
15
  const fullComponent = <UDExSelectBox
5
16
  id="select-box"
6
17
  height="400px"
@@ -1071,4 +1082,56 @@ describe("SelectBox", () => {
1071
1082
  });
1072
1083
  });
1073
1084
  });
1085
+
1086
+ describe("Select Box Rating Indicator", () => {
1087
+ it("renders rating indicator in dropdown items when rating is set", () => {
1088
+ cy.mount(selectBoxWithRating);
1089
+ cy.get("#select-box").shadow().find(".udex-text-field__trigger").click();
1090
+
1091
+ cy.get("#select-box").shadow().find("udex-list-item").eq(0).shadow()
1092
+ .find("[udex-rating-indicator]")
1093
+ .should("exist");
1094
+ });
1095
+
1096
+ it("passes correct rating value to dropdown list items", () => {
1097
+ cy.mount(selectBoxWithRating);
1098
+ cy.get("#select-box").shadow().find(".udex-text-field__trigger").click();
1099
+
1100
+ cy.get("#select-box").shadow().find("udex-list-item").eq(0)
1101
+ .should("have.attr", "rating", "5");
1102
+
1103
+ cy.get("#select-box").shadow().find("udex-list-item").eq(1)
1104
+ .should("have.attr", "rating", "4");
1105
+ });
1106
+
1107
+ it("passes correct rating-count and total-count to dropdown list items", () => {
1108
+ cy.mount(selectBoxWithRating);
1109
+ cy.get("#select-box").shadow().find(".udex-text-field__trigger").click();
1110
+
1111
+ cy.get("#select-box").shadow().find("udex-list-item").eq(0)
1112
+ .should("have.attr", "rating-count", "5.0")
1113
+ .and("have.attr", "total-count", "23");
1114
+ });
1115
+
1116
+ it("MultiSelect mode - renders tag with rating text when selecting a rating item", () => {
1117
+ cy.mount(selectBoxWithRating);
1118
+ cy.get("#select-box").shadow().find(".udex-text-field__trigger").click();
1119
+
1120
+ cy.get("#select-box").shadow().find("udex-list-item").eq(0).click();
1121
+
1122
+ cy.get("#select-box").shadow()
1123
+ .find("udex-tag-group").shadow()
1124
+ .find("udex-tag")
1125
+ .should("exist");
1126
+ });
1127
+
1128
+ it("filters rating items by ratingCount when searching", () => {
1129
+ cy.mount(selectBoxWithRating);
1130
+ cy.get("#select-box").shadow().find(".udex-text-field__search").click();
1131
+ cy.realType("5.0");
1132
+
1133
+ cy.get("#select-box").shadow().find("udex-list-item")
1134
+ .should("have.length", 1);
1135
+ });
1136
+ });
1074
1137
  });
@@ -2,6 +2,7 @@ import type { UDExListItemBaseEvents } from "./ListItemBase.js";
2
2
  import UDExListItemBase from "./ListItemBase.js";
3
3
  import { UDExAvatarShape, UDExAvatarSize } from "./Avatar.js";
4
4
  import UDExButton from "./Button.js";
5
+ import { UDExDisplayType } from "./RatingIndicator.js";
5
6
  import type UDExStatusBadgeValueState from "./types/StatusBadgeValueState.js";
6
7
  import { UDExAdditionalTextState } from "./types/List.js";
7
8
  import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
@@ -142,6 +143,7 @@ declare class UDExListItem extends UDExListItemBase {
142
143
  */
143
144
  accessibleRole: string;
144
145
  _additionalClassName: string;
146
+ _displayRating: UDExDisplayType;
145
147
  deactivate: () => void;
146
148
  static i18nBundle: I18nBundle;
147
149
  constructor();
@@ -172,6 +174,7 @@ declare class UDExListItem extends UDExListItemBase {
172
174
  get stateAccessibleName(): string;
173
175
  get itemDescribedBy(): string;
174
176
  get itemState(): string;
177
+ get ratingAriaLabel(): string;
175
178
  }
176
179
  export default UDExListItem;
177
180
  export type { UDExListItemBaseEventDetail, UDExListItemItemPressEventDetail };