@sapudex/web-components 2.29.0 → 2.30.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 (172) hide show
  1. package/cypress/specs/FilterMenu.cy.tsx +39 -1
  2. package/cypress/specs/List.cy.tsx +171 -0
  3. package/cypress/specs/ReviewItem.cy.tsx +48 -14
  4. package/cypress/support/component-imports.instrumented.ts +4 -0
  5. package/cypress/support/component-imports.src.ts +5 -1
  6. package/dist/Avatar.d.ts +1 -0
  7. package/dist/Avatar.js +9 -0
  8. package/dist/Avatar.js.map +1 -1
  9. package/dist/FilterMenu.d.ts +2 -0
  10. package/dist/FilterMenu.js +2 -0
  11. package/dist/FilterMenu.js.map +1 -1
  12. package/dist/ImageGridTickerTemplate.js +2 -1
  13. package/dist/ImageGridTickerTemplate.js.map +1 -1
  14. package/dist/List.d.ts +4 -2
  15. package/dist/List.js +42 -9
  16. package/dist/List.js.map +1 -1
  17. package/dist/ListGroupHeader.d.ts +4 -0
  18. package/dist/ListGroupHeader.js +26 -1
  19. package/dist/ListGroupHeader.js.map +1 -1
  20. package/dist/ListGroupHeaderBase.js +1 -1
  21. package/dist/ListGroupHeaderBase.js.map +1 -1
  22. package/dist/ListItem.js +1 -1
  23. package/dist/ListItem.js.map +1 -1
  24. package/dist/ListItemBase.d.ts +40 -1
  25. package/dist/ListItemBase.js +70 -3
  26. package/dist/ListItemBase.js.map +1 -1
  27. package/dist/MastheadItem.d.ts +9 -2
  28. package/dist/MastheadItem.js +19 -15
  29. package/dist/MastheadItem.js.map +1 -1
  30. package/dist/MastheadMenu.d.ts +2 -2
  31. package/dist/MastheadMenu.js +9 -9
  32. package/dist/MastheadMenu.js.map +1 -1
  33. package/dist/Pagination.js +1 -1
  34. package/dist/Pagination.js.map +1 -1
  35. package/dist/ReviewItem.d.ts +8 -7
  36. package/dist/ReviewItem.js +35 -36
  37. package/dist/ReviewItem.js.map +1 -1
  38. package/dist/ReviewItemTemplate.js +3 -1
  39. package/dist/ReviewItemTemplate.js.map +1 -1
  40. package/dist/SearchableListBase.d.ts +3 -0
  41. package/dist/SearchableListBase.js +37 -1
  42. package/dist/SearchableListBase.js.map +1 -1
  43. package/dist/SelectBox.js +0 -1
  44. package/dist/SelectBox.js.map +1 -1
  45. package/dist/TextField.d.ts +1 -1
  46. package/dist/TextField.js.map +1 -1
  47. package/dist/assets/{InputSuggestions-GMbG8xQ2.js → InputSuggestions-CaAXs3ok.js} +1 -1
  48. package/dist/assets/{ListItemStandardExpandableTextTemplate-IyZB_qzJ.js → ListItemStandardExpandableTextTemplate-CPqUBd--.js} +1 -1
  49. package/dist/assets/{bundle.esm-Dg7j0PXg.js → bundle.esm-L-7H6J4f.js} +213 -213
  50. package/dist/assets/messagebundle_en-J-MHTtdL.js +1 -0
  51. package/dist/css/themes/FilterMenu.css +1 -1
  52. package/dist/css/themes/ImageGridTicker.css +1 -1
  53. package/dist/css/themes/ListGroupHeader.css +1 -1
  54. package/dist/css/themes/ListItem.css +1 -1
  55. package/dist/css/themes/MastheadCountrySelector.css +1 -1
  56. package/dist/css/themes/MastheadItem.css +1 -1
  57. package/dist/css/themes/MastheadProfileMenu.css +1 -1
  58. package/dist/css/themes/Modal.css +1 -1
  59. package/dist/css/themes/RatingIndicator.css +1 -1
  60. package/dist/css/themes/ReviewItem.css +1 -1
  61. package/dist/css/themes/Slider.css +1 -1
  62. package/dist/custom-elements-internal.json +965 -17
  63. package/dist/custom-elements.json +712 -10
  64. package/dist/generated/assets/i18n/messagebundle_en.json +1 -1
  65. package/dist/generated/i18n/i18n-defaults.d.ts +5 -1
  66. package/dist/generated/i18n/i18n-defaults.js +5 -1
  67. package/dist/generated/i18n/i18n-defaults.js.map +1 -1
  68. package/dist/generated/templates/ListGroupHeaderTemplate.lit.js +21 -10
  69. package/dist/generated/templates/ListGroupHeaderTemplate.lit.js.map +1 -1
  70. package/dist/generated/templates/ListItemTemplate.lit.js +21 -19
  71. package/dist/generated/templates/ListItemTemplate.lit.js.map +1 -1
  72. package/dist/generated/templates/MastheadCountrySelectorTemplate.lit.js +1 -1
  73. package/dist/generated/templates/MastheadCountrySelectorTemplate.lit.js.map +1 -1
  74. package/dist/generated/templates/MastheadItemTemplate.lit.js +33 -25
  75. package/dist/generated/templates/MastheadItemTemplate.lit.js.map +1 -1
  76. package/dist/generated/templates/MastheadMobileMenuTemplate.lit.js +5 -5
  77. package/dist/generated/templates/MastheadMobileMenuTemplate.lit.js.map +1 -1
  78. package/dist/generated/templates/SearchableListBaseTemplate.lit.js +1 -1
  79. package/dist/generated/templates/SearchableListBaseTemplate.lit.js.map +1 -1
  80. package/dist/generated/themes/FilterMenu.css.d.ts +1 -1
  81. package/dist/generated/themes/FilterMenu.css.js +1 -1
  82. package/dist/generated/themes/FilterMenu.css.js.map +1 -1
  83. package/dist/generated/themes/ImageGridTicker.css.d.ts +1 -1
  84. package/dist/generated/themes/ImageGridTicker.css.js +1 -1
  85. package/dist/generated/themes/ImageGridTicker.css.js.map +1 -1
  86. package/dist/generated/themes/ListGroupHeader.css.d.ts +1 -1
  87. package/dist/generated/themes/ListGroupHeader.css.js +1 -1
  88. package/dist/generated/themes/ListGroupHeader.css.js.map +1 -1
  89. package/dist/generated/themes/ListItem.css.d.ts +1 -1
  90. package/dist/generated/themes/ListItem.css.js +1 -1
  91. package/dist/generated/themes/ListItem.css.js.map +1 -1
  92. package/dist/generated/themes/MastheadCountrySelector.css.d.ts +1 -1
  93. package/dist/generated/themes/MastheadCountrySelector.css.js +1 -1
  94. package/dist/generated/themes/MastheadCountrySelector.css.js.map +1 -1
  95. package/dist/generated/themes/MastheadItem.css.d.ts +1 -1
  96. package/dist/generated/themes/MastheadItem.css.js +1 -1
  97. package/dist/generated/themes/MastheadItem.css.js.map +1 -1
  98. package/dist/generated/themes/MastheadProfileMenu.css.d.ts +1 -1
  99. package/dist/generated/themes/MastheadProfileMenu.css.js +1 -1
  100. package/dist/generated/themes/MastheadProfileMenu.css.js.map +1 -1
  101. package/dist/generated/themes/Modal.css.d.ts +1 -1
  102. package/dist/generated/themes/Modal.css.js +1 -1
  103. package/dist/generated/themes/Modal.css.js.map +1 -1
  104. package/dist/generated/themes/RatingIndicator.css.d.ts +1 -1
  105. package/dist/generated/themes/RatingIndicator.css.js +1 -1
  106. package/dist/generated/themes/RatingIndicator.css.js.map +1 -1
  107. package/dist/generated/themes/ReviewItem.css.d.ts +1 -1
  108. package/dist/generated/themes/ReviewItem.css.js +1 -1
  109. package/dist/generated/themes/ReviewItem.css.js.map +1 -1
  110. package/dist/generated/themes/Slider.css.d.ts +1 -1
  111. package/dist/generated/themes/Slider.css.js +1 -1
  112. package/dist/generated/themes/Slider.css.js.map +1 -1
  113. package/dist/messagebundle.properties +6 -0
  114. package/dist/messagebundle_en.properties +2 -0
  115. package/dist/types/SearchableListBase.d.ts +1 -0
  116. package/dist/types/SearchableListBase.js.map +1 -1
  117. package/dist/vscode.html-custom-data.json +122 -12
  118. package/dist/web-types.json +271 -11
  119. package/package.json +1 -1
  120. package/src/Avatar.ts +9 -0
  121. package/src/FilterMenu.ts +4 -0
  122. package/src/ImageGridTickerTemplate.tsx +3 -1
  123. package/src/List.ts +48 -11
  124. package/src/ListGroupHeader.hbs +130 -15
  125. package/src/ListGroupHeader.ts +31 -1
  126. package/src/ListGroupHeaderBase.ts +1 -1
  127. package/src/ListItem.hbs +15 -0
  128. package/src/ListItem.ts +1 -1
  129. package/src/ListItemBase.ts +107 -1
  130. package/src/MastheadCountrySelector.hbs +1 -1
  131. package/src/MastheadItem.hbs +31 -23
  132. package/src/MastheadItem.ts +20 -18
  133. package/src/MastheadMenu.ts +10 -9
  134. package/src/MastheadMobileMenu.hbs +5 -5
  135. package/src/Pagination.ts +1 -1
  136. package/src/ReviewItem.ts +45 -35
  137. package/src/ReviewItemTemplate.tsx +16 -14
  138. package/src/SearchableListBase.hbs +3 -0
  139. package/src/SearchableListBase.ts +52 -1
  140. package/src/SelectBox.ts +0 -1
  141. package/src/TextField.ts +1 -1
  142. package/src/generated/i18n/i18n-defaults.ts +2 -2
  143. package/src/generated/templates/ListGroupHeaderTemplate.lit.ts +21 -10
  144. package/src/generated/templates/ListItemTemplate.lit.ts +21 -19
  145. package/src/generated/templates/MastheadCountrySelectorTemplate.lit.ts +1 -1
  146. package/src/generated/templates/MastheadItemTemplate.lit.ts +33 -25
  147. package/src/generated/templates/MastheadMobileMenuTemplate.lit.ts +5 -5
  148. package/src/generated/templates/SearchableListBaseTemplate.lit.ts +1 -1
  149. package/src/generated/themes/FilterMenu.css.ts +1 -1
  150. package/src/generated/themes/ImageGridTicker.css.ts +1 -1
  151. package/src/generated/themes/ListGroupHeader.css.ts +1 -1
  152. package/src/generated/themes/ListItem.css.ts +1 -1
  153. package/src/generated/themes/MastheadCountrySelector.css.ts +1 -1
  154. package/src/generated/themes/MastheadItem.css.ts +1 -1
  155. package/src/generated/themes/MastheadProfileMenu.css.ts +1 -1
  156. package/src/generated/themes/Modal.css.ts +1 -1
  157. package/src/generated/themes/RatingIndicator.css.ts +1 -1
  158. package/src/generated/themes/ReviewItem.css.ts +1 -1
  159. package/src/generated/themes/Slider.css.ts +1 -1
  160. package/src/i18n/messagebundle.properties +6 -0
  161. package/src/i18n/messagebundle_en.properties +2 -0
  162. package/src/themes/FilterMenu.css +2 -2
  163. package/src/themes/ImageGridTicker.css +2 -26
  164. package/src/themes/ListGroupHeader.css +103 -8
  165. package/src/themes/ListItem.css +67 -15
  166. package/src/themes/MastheadCountrySelector.css +2 -0
  167. package/src/themes/MastheadItem.css +31 -3
  168. package/src/themes/MastheadProfileMenu.css +1 -9
  169. package/src/themes/Modal.css +2 -1
  170. package/src/themes/ReviewItem.css +18 -1
  171. package/src/types/SearchableListBase.ts +1 -0
  172. package/dist/assets/messagebundle_en-DfoXjU7o.js +0 -1
@@ -21,23 +21,21 @@
21
21
  </span>
22
22
  {{/if}}
23
23
  {{else if isProfileBtn}}
24
- <span
24
+ <button
25
25
  id="{{this._id}}"
26
- class="udex-masthead-item__btn {{additionalClass}}"
27
- aria-expanded="{{selected}}"
26
+ class="udex-masthead-item__btn udex-masthead-item__profile-btn {{additionalClass}}"
27
+ aria-expanded="{{buttonAriaExpanded}}"
28
28
  aria-describedby="{{ariaDescribedBy}}"
29
29
  aria-controls="{{this._id}}_dropdown"
30
- aria-haspopup="{{hasDropdownSlot}}"
31
30
  aria-label="{{accessibleLabel}}"
32
31
  @keydown="{{handleKeyDown}}"
33
32
  @click="{{handleItemClick}}"
33
+ @click-default="{{handleItemClick}}"
34
34
  @mousedown="{{handleMouseDown}}"
35
35
  @mouseup="{{handleMouseUp}}">
36
36
  {{> content}}
37
- </span>
38
- {{#if isDropdownOpened}}
39
- {{> dropdown}}
40
- {{/if}}
37
+ </button>
38
+ {{> dropdown}}
41
39
  {{else}}
42
40
  <button
43
41
  role="{{accessibleRoleName}}"
@@ -46,7 +44,6 @@
46
44
  aria-expanded="{{buttonAriaExpanded}}"
47
45
  aria-controls="{{this._id}}_dropdown"
48
46
  aria-describedby="{{ariaDescribedBy}}"
49
- aria-haspopup="{{hasDropdownSlot}}"
50
47
  aria-label="{{accessibleLabel}}"
51
48
  @keydown="{{handleKeyDown}}"
52
49
  @mousedown="{{handleMouseDown}}"
@@ -54,9 +51,7 @@
54
51
  @click="{{handleItemClick}}">
55
52
  {{> content}}
56
53
  </button>
57
- {{#if isDropdownOpened}}
58
- {{> dropdown}}
59
- {{/if}}
54
+ {{> dropdown}}
60
55
  {{/if}}
61
56
 
62
57
  {{#*inline "content"}}
@@ -69,14 +64,23 @@
69
64
  mode="Decorative">
70
65
  </ui5-icon>
71
66
  {{else if image}}
72
- <div class="udex-masthead-item__image-wrap">
73
- <img
74
- class="udex-masthead-item__image"
75
- id="{{this._id}}_image-alt"
76
- src="{{image}}"
77
- aria-hidden="{{isCountrySelectorBtn}}"
78
- alt="{{imageAlt}}"/>
79
- </div>
67
+ {{#if imageLoadError}}
68
+ <ui5-icon
69
+ class="udex-masthead-item__icon"
70
+ name="sap-icon://world"
71
+ mode="Decorative">
72
+ </ui5-icon>
73
+ {{else}}
74
+ <div class="udex-masthead-item__image-wrap">
75
+ <img
76
+ class="udex-masthead-item__image"
77
+ id="{{this._id}}_image-alt"
78
+ src="{{image}}"
79
+ aria-hidden="{{isCountrySelectorBtn}}"
80
+ alt="{{imageAlt}}"
81
+ @error="{{handleImageError}}"/>
82
+ </div>
83
+ {{/if}}
80
84
  {{#if imageAlt}}
81
85
  <span
82
86
  id="image-desc"
@@ -99,13 +103,17 @@
99
103
  {{/inline}}
100
104
 
101
105
  {{#*inline "dropdown"}}
106
+ {{#if isDropdownOpened}}
102
107
  <div
103
108
  id="{{this._id}}_dropdown"
104
109
  class="udex-masthead-item__dropdown"
105
110
  @keydown="{{handleKeyDown}}"
106
111
  @focusout="{{handleFocusout}}">
107
- <div class="udex-masthead-item__dropdown-holder">
108
- <slot name="dropdown"></slot>
109
- </div>
112
+ <div class="udex-masthead-item__dropdown-holder">
113
+ <slot name="dropdown"></slot>
114
+ </div>
110
115
  </div>
116
+ {{else}}
117
+ <div id="{{this._id}}_dropdown"></div>
118
+ {{/if}}
111
119
  {{/inline}}
@@ -10,13 +10,13 @@ import "@ui5/webcomponents-icons/dist/search.js";
10
10
  import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
11
11
  import { isEscape } from "@ui5/webcomponents-base/dist/Keys.js";
12
12
  import { isDesktop } from "@ui5/webcomponents-base/dist/Device.js";
13
- import { getFirstFocusableElement } from "@ui5/webcomponents-base/dist/util/FocusableElements.js";
14
13
  import {
15
14
  OPENS_IN_A_NEW_WINDOW, PROFILE, CART, COUNTRY_SELECTOR, SUPPORT, SEARCH,
16
15
  } from "./generated/i18n/i18n-defaults.js";
17
16
  import UDExMastheadMenu from "./MastheadMenu.js";
18
17
  import type UDExAvatar from "./Avatar.js";
19
18
  import "@ui5/webcomponents-icons/dist/slim-arrow-down.js";
19
+ import "@ui5/webcomponents-icons/dist/world.js";
20
20
  import type UDExMastheadShell from "./MastheadShell.js";
21
21
  import MastheadItemTemplate from "./generated/templates/MastheadItemTemplate.lit.js";
22
22
  import { UDExTarget } from "./types/common.js";
@@ -315,6 +315,14 @@ class UDExMastheadItem extends UI5Element implements IMastheadItem {
315
315
  @property({ type: Boolean })
316
316
  _isMenuCloseBtn?: boolean;
317
317
 
318
+ /**
319
+ * Defines if the image failed to load
320
+ *
321
+ * @private
322
+ */
323
+ @property({ type: Boolean })
324
+ _imageLoadError = false;
325
+
318
326
  @i18n("@sapudex/web-components")
319
327
  static i18nBundle: I18nBundle;
320
328
 
@@ -339,10 +347,6 @@ class UDExMastheadItem extends UI5Element implements IMastheadItem {
339
347
  this.setDropdownInsetStart();
340
348
  }
341
349
 
342
- if (this.shouldFocusFirstElement) {
343
- this.applyFocusToFirstElement();
344
- }
345
-
346
350
  this.handleOutsideClick();
347
351
  }
348
352
 
@@ -432,6 +436,10 @@ class UDExMastheadItem extends UI5Element implements IMastheadItem {
432
436
  }
433
437
  }
434
438
 
439
+ handleImageError(): void {
440
+ this._imageLoadError = true;
441
+ }
442
+
435
443
  fireDropdownEvents(): void {
436
444
  if (this.hasDropdownSlot) {
437
445
  const { Open, Close } = UDExMastheadDropdownItemAction;
@@ -511,13 +519,6 @@ class UDExMastheadItem extends UI5Element implements IMastheadItem {
511
519
  this.fireDropdownCloseEvent(this.accessibleName || this.eventItemTitle, UDExMastheadDropdownItemAction.Close);
512
520
  }
513
521
 
514
- async applyFocusToFirstElement(): Promise<void> {
515
- const focusedElement = await getFirstFocusableElement(this.dropdownHolder);
516
- if (focusedElement) {
517
- focusedElement.focus();
518
- }
519
- }
520
-
521
522
  getDomElement(selector: string): HTMLElement | undefined | null {
522
523
  return this.shadowRoot?.querySelector(`${selector}`);
523
524
  }
@@ -607,10 +608,6 @@ class UDExMastheadItem extends UI5Element implements IMastheadItem {
607
608
  return labels.length ? labels.join(" ").trim() : undefined;
608
609
  }
609
610
 
610
- get shouldFocusFirstElement(): boolean {
611
- return this.isDropdownOpened && !this._preventFocusOut;
612
- }
613
-
614
611
  get isDropdownOpened(): boolean {
615
612
  return this.hasDropdownSlot && this.selected;
616
613
  }
@@ -688,9 +685,14 @@ class UDExMastheadItem extends UI5Element implements IMastheadItem {
688
685
  }
689
686
 
690
687
  get buttonAriaExpanded(): boolean | undefined {
691
- if (!this.isSearchBtn) {
692
- return this.selected;
688
+ if (!this.hasDropdownSlot) {
689
+ return undefined;
693
690
  }
691
+ return this.selected || false;
692
+ }
693
+
694
+ get imageLoadError(): boolean {
695
+ return this._imageLoadError && this.isCountrySelectorBtn;
694
696
  }
695
697
  }
696
698
 
@@ -9,7 +9,7 @@ import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
9
9
  import "@ui5/webcomponents-icons/dist/slim-arrow-right.js";
10
10
  import "@ui5/webcomponents-icons/dist/action.js";
11
11
  import {
12
- isUp, isDown, isTabNext, isTabPrevious, isHome, isEnd,
12
+ isTabNext, isTabPrevious, isHome, isEnd,
13
13
  } from "@ui5/webcomponents-base/dist/Keys.js";
14
14
  import UDExBadge from "./Badge.js";
15
15
  import UDExMastheadMenuItem, { UDExItemType } from "./MastheadMenuItem.js";
@@ -139,11 +139,11 @@ class UDExMastheadMenu extends UI5Element {
139
139
  const isFirstItem = e.target === this.links[0];
140
140
  const isLastItem = e.target === this.links[this.links.length - 1];
141
141
 
142
- if (isUp(e) || (isTabPrevious(e) && !isFirstItem)) {
143
- this.handleUpArrow(e);
142
+ if (isTabPrevious(e) && !isFirstItem) {
143
+ this.handlePreviousItemFocus(e);
144
144
  }
145
- if (isDown(e) || (isTabNext(e) && !isLastItem)) {
146
- this.handleDownArrow(e);
145
+ if (isTabNext(e) && !isLastItem) {
146
+ this.handleNextItemFocus(e);
147
147
  }
148
148
  if (isHome(e)) {
149
149
  this.handleHomeKey(e);
@@ -165,13 +165,13 @@ class UDExMastheadMenu extends UI5Element {
165
165
  lastItem.focus();
166
166
  }
167
167
 
168
- handleUpArrow(e: KeyboardEvent): void {
168
+ handlePreviousItemFocus(e: KeyboardEvent): void {
169
169
  e.preventDefault();
170
170
  const currentItem = e.target as UDExMastheadMenuItem;
171
171
  this.focusPreviousItem(currentItem);
172
172
  }
173
173
 
174
- handleDownArrow(e: KeyboardEvent): void {
174
+ handleNextItemFocus(e: KeyboardEvent): void {
175
175
  e.preventDefault();
176
176
  const currentItem = e.target as UDExMastheadMenuItem;
177
177
  this.focusNextItem(currentItem);
@@ -239,10 +239,11 @@ class UDExMastheadMenu extends UI5Element {
239
239
  return parsedItems.map((mainItem: MenuItem) => {
240
240
  mainItem.subMenuLinkItems = mainItem.subMenuLinkItems.map((subMenu: SubMenuLinkItem) => {
241
241
  subMenu.categoryId = `${id++}`;
242
- subMenu.items = subMenu.items.map((item: Item) => ({
242
+
243
+ subMenu.items = subMenu.items ? subMenu.items.map((item: Item) => ({
243
244
  ...item,
244
245
  ...(item.title === "-" && { isDivider: true }),
245
- }));
246
+ })) : [];
246
247
 
247
248
  return subMenu;
248
249
  });
@@ -38,7 +38,7 @@
38
38
  class="udex-masthead-mobile-menu__back-item"
39
39
  @click="{{handleBackToMenuItems}}"
40
40
  label="{{_clickedItem.innerText}}"
41
- aria-has-popup="true"
41
+ aria-haspopup="true"
42
42
  type="Button"
43
43
  is-back
44
44
  has-border
@@ -58,7 +58,7 @@
58
58
  @click="{{handleBackToCategories}}"
59
59
  label="{{_category.category}}"
60
60
  type="Button"
61
- aria-has-popup="true"
61
+ aria-haspopup="true"
62
62
  has-border
63
63
  icon-start
64
64
  is-back
@@ -77,7 +77,7 @@
77
77
  @click="{{handleExploreSapClick}}"
78
78
  @keydown="{{handleLeftRightKeys}}"
79
79
  accessible-role-text="button"
80
- aria-has-popup="true"
80
+ aria-haspopup="true"
81
81
  type="Button"
82
82
  first-level-btn
83
83
  first-level
@@ -96,7 +96,7 @@
96
96
  part="menu-item"
97
97
  @click="{{../handleMenuItemClick}}"
98
98
  label="{{this.innerText}}"
99
- aria-has-popup="true"
99
+ aria-haspopup="true"
100
100
  id="{{this._id}}"
101
101
  type="Button"
102
102
  first-level-btn
@@ -141,7 +141,7 @@
141
141
  @click="{{../handleMenuCategoryClick}}"
142
142
  id="{{@index}}"
143
143
  label="{{this.category}}"
144
- aria-has-popup="true"
144
+ aria-haspopup="true"
145
145
  type="Button"
146
146
  second-level-btn
147
147
  second-level
package/src/Pagination.ts CHANGED
@@ -364,7 +364,7 @@ class UDExPagination extends UI5Element {
364
364
  return;
365
365
  }
366
366
 
367
- if (!this._pages.length || this._visiblePages.length < this.displayNumber) {
367
+ if (!this._pages.length || this._visiblePages.length !== this.displayNumber) {
368
368
  this._firstPageIndex = 0;
369
369
  this._lastPageIndex = pages > this.displayNumber ? this.displayNumber : pages;
370
370
  this._pageFocusedManually = false;
package/src/ReviewItem.ts CHANGED
@@ -8,7 +8,7 @@ import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
8
8
  import type { ResizeObserverCallback } from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
9
9
  import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
10
10
  import ReviewItemTemplate from "./ReviewItemTemplate.js";
11
- import { READ_LESS, READ_MORE } from "./generated/i18n/i18n-defaults.js";
11
+ import { SHOW_LESS, SHOW_MORE } from "./generated/i18n/i18n-defaults.js";
12
12
  import translate from "./utils/translate.js";
13
13
 
14
14
  // Styles
@@ -73,14 +73,6 @@ class UDExReviewItem extends UI5Element {
73
73
  @property({ type: String })
74
74
  text!: string;
75
75
 
76
- /**
77
- * Defines whether button for showing truncated text is shown.
78
- * @default 2
79
- * @private
80
- */
81
- @property({ type: Number })
82
- _textLineLimit: number = 2;
83
-
84
76
  /**
85
77
  * Defines whether the text is expanded.
86
78
  * @default false
@@ -123,23 +115,30 @@ class UDExReviewItem extends UI5Element {
123
115
  @slot()
124
116
  date!: Array<HTMLElement>;
125
117
 
118
+ /**
119
+ * Slot for custom content.
120
+ *
121
+ * @slot
122
+ */
123
+ @slot()
124
+ content?: Array<HTMLElement>;
125
+
126
126
  @i18n("@sapudex/web-components")
127
127
  static i18nBundle: I18nBundle;
128
128
 
129
129
  _handleResizeBound: ResizeObserverCallback;
130
130
  _textElement!: HTMLElement;
131
131
  _visibleText!: string;
132
- _maxHeight: number = 48;
132
+ _maxHeight: number = 90;
133
133
  _maxRating: number = 5;
134
134
  _minRating: number = 0;
135
135
 
136
136
  constructor() {
137
137
  super();
138
- this._handleResizeBound = this._checkTextOverflow.bind(this);
138
+ this._handleResizeBound = this._handleResize.bind(this);
139
139
  }
140
140
 
141
141
  onEnterDOM(): void {
142
- this._maxHeight = 48;
143
142
  this._completeText = this.text;
144
143
  ResizeHandler.register(this, this._handleResizeBound);
145
144
  }
@@ -149,19 +148,22 @@ class UDExReviewItem extends UI5Element {
149
148
  }
150
149
 
151
150
  onAfterRendering(): void {
152
- this._textElement = this.getTextElement();
153
- this._checkTextOverflow();
151
+ this._textElement = this.textElement;
152
+ if (this.text) {
153
+ this._checkTextOverflow();
154
+ }
155
+ }
156
+
157
+ _handleResize(): void {
158
+ if (this.text) {
159
+ this._checkTextOverflow();
160
+ }
154
161
  }
155
162
 
156
163
  _toggleText() {
157
164
  this._textExpanded = !this._textExpanded;
158
- if (this._textExpanded) {
159
- this._completeText = this.text;
160
- this._textElement.classList.add("udex-review-item__text-expanded");
161
- } else {
162
- this._textElement.classList.remove("udex-review-item__text-expanded");
163
- this._completeText = this._visibleText;
164
- }
165
+ this._completeText = this._textExpanded ? this.text : this._visibleText;
166
+ this._textElement.classList.toggle("udex-review-item__text-expanded", this._textExpanded);
165
167
  }
166
168
 
167
169
  _checkTextOverflow() {
@@ -169,18 +171,26 @@ class UDExReviewItem extends UI5Element {
169
171
 
170
172
  const canvas = document.createElement("canvas");
171
173
  const context = canvas.getContext("2d")!;
172
- context.font = `16px sans-serif`;
174
+ context.font = "16px sans-serif";
173
175
 
174
- let currentLineWidth = 0;
176
+ const maxWidth = this._textElement.offsetWidth;
175
177
  let visibleText = "";
178
+ const lineHeight = 22; // Approximate line height for 16px font
179
+
180
+ for (let i = 0; i < words.length; i++) {
181
+ const testText = visibleText + (visibleText ? " " : "") + words[i];
182
+ const metrics = context.measureText(testText);
183
+ const textWidth = metrics.width;
184
+
185
+ const estimatedLines = Math.ceil(textWidth / maxWidth);
186
+ const estimatedHeight = estimatedLines * lineHeight;
176
187
 
177
- words.forEach(word => {
178
- const wordWidth = context.measureText(`${word} `).width;
179
- if (currentLineWidth + wordWidth <= 2 * this._textElement.offsetWidth) {
180
- visibleText += `${word} `;
181
- currentLineWidth += wordWidth;
188
+ if (estimatedHeight > this._maxHeight) {
189
+ break;
182
190
  }
183
- });
191
+
192
+ visibleText = testText;
193
+ }
184
194
 
185
195
  this._visibleText = visibleText;
186
196
  if (!this._textExpanded) {
@@ -190,8 +200,13 @@ class UDExReviewItem extends UI5Element {
190
200
  this._showTextExpander = (!this._textExpanded && this._completeText.length < this.text.length) || (this._textExpanded && visibleText.length < this.text.length);
191
201
  }
192
202
 
203
+ get textElement(): HTMLElement {
204
+ const textElement = this.shadowRoot?.getElementById("udex-review-item__text");
205
+ return textElement!;
206
+ }
207
+
193
208
  get buttonText(): string {
194
- return this._textExpanded ? translate(UDExReviewItem, READ_LESS) : translate(UDExReviewItem, READ_MORE);
209
+ return this._textExpanded ? translate(UDExReviewItem, SHOW_LESS) : translate(UDExReviewItem, SHOW_MORE);
195
210
  }
196
211
 
197
212
  get reviewItemClasses(): string {
@@ -210,11 +225,6 @@ class UDExReviewItem extends UI5Element {
210
225
  }
211
226
  return this.rating;
212
227
  }
213
-
214
- getTextElement(): HTMLElement {
215
- const textElement = this.shadowRoot?.getElementById("udex-review-item__text");
216
- return textElement!;
217
- }
218
228
  }
219
229
 
220
230
  UDExReviewItem.define();
@@ -24,21 +24,23 @@ export default function ReviewItemTemplate(this: ReviewItem) {
24
24
  )}
25
25
  </div>
26
26
  <div className={`${rootClass}__text-container`}>
27
- <div className={`${rootClass}__title`}>{this.reviewTitle}</div>
27
+ {this.reviewTitle &&
28
+ <div className={`${rootClass}__title`}>{this.reviewTitle}</div>
29
+ }
28
30
  <div className={`${rootClass}__text-wrapper`}>
29
- <p className={`${rootClass}__text`} id={`${rootClass}__text`}>
30
- {this._completeText}
31
- </p>
32
- {this._showTextExpander && (
33
- <UDExButton
34
- id="reviewExpander"
35
- class={`${rootClass}__text-expander`}
36
- design={UDExButtonDesign.Invisible}
37
- size={UDExButtonSize.Large}
38
- onClick={this._toggleText.bind(this)}>
39
- {this.buttonText}
40
- </UDExButton>
41
- )}
31
+ <slot slot="content" name="content">
32
+ <p className={`${rootClass}__text`} id={`${rootClass}__text`}>{this._completeText}</p>
33
+ {this._showTextExpander &&
34
+ <UDExButton
35
+ id="reviewExpander"
36
+ class={`${rootClass}__text-expander`}
37
+ design={UDExButtonDesign.Invisible}
38
+ size={UDExButtonSize.Large}
39
+ onClick={this._toggleText.bind(this)}>
40
+ {this.buttonText}
41
+ </UDExButton>
42
+ }
43
+ </slot>
42
44
  </div>
43
45
  {this.author && (
44
46
  <slot slot="author" name="author" className={`${rootClass}__author`}></slot>
@@ -77,6 +77,9 @@
77
77
  label="{{this.item.label}}"
78
78
  supporting-text="{{this.item.supportingText}}"
79
79
  ?controlled="{{this.item.controlled}}"
80
+ ?collapsible="{{this.item.collapsible}}"
81
+ ?collapsed="{{this.item.collapsed}}"
82
+ ?_filtered="{{../_shouldFilterItems}}"
80
83
  ?disabled="{{this.item.disabled}}"
81
84
  ?selected="{{this.item.selected}}"
82
85
  @ui5-first-icon-click="{{../fireFirstIconClickEvent}}"
@@ -252,11 +252,13 @@ class UDExSearchableListBase extends UI5Element {
252
252
 
253
253
  _shouldFilterItems?: boolean;
254
254
  _accSearchResultInfo?: string;
255
+ _groupCollapseStateBeforeSearch: Map<string, boolean>;
255
256
 
256
257
  constructor() {
257
258
  super();
258
259
  this._shouldFilterItems = false;
259
260
  this._filteredItems = [];
261
+ this._groupCollapseStateBeforeSearch = new Map();
260
262
  }
261
263
 
262
264
  onBeforeRendering() {
@@ -332,7 +334,17 @@ class UDExSearchableListBase extends UI5Element {
332
334
 
333
335
  onSearch(value: string) {
334
336
  const searchValue: string = value;
335
- this._shouldFilterItems = true;
337
+ const hasSearchQuery = searchValue !== "";
338
+
339
+ if (hasSearchQuery && !this._shouldFilterItems) {
340
+ this.saveGroupCollapseState();
341
+ }
342
+
343
+ if (!hasSearchQuery && this._shouldFilterItems) {
344
+ this.restoreGroupCollapseState();
345
+ }
346
+
347
+ this._shouldFilterItems = hasSearchQuery;
336
348
  this._filteredItems = this.filterItems(this.getProcessedItems(), searchValue);
337
349
  const items = this.getFlattenItemsArray(this._filteredItems);
338
350
  this._accSearchResultInfo = items.length ? `${items.length} ${this.resultReturnedText}` : this.noMatchesFoundText;
@@ -361,16 +373,27 @@ class UDExSearchableListBase extends UI5Element {
361
373
  resetSearch() {
362
374
  const search = this.getSearch();
363
375
  search.value = "";
376
+ this._shouldFilterItems = false;
377
+ this.restoreGroupCollapseState();
364
378
  this._filteredItems = this.filterItems(this.getProcessedItems(), "");
365
379
  }
366
380
 
367
381
  filterItems(items: Array<UDExProcessedItem>, str: string): Array<UDExProcessedItem> {
382
+ const hasSearchQuery = str !== "";
383
+
368
384
  return items.filter(preparedItem => {
369
385
  const { isGroup, item } = preparedItem;
386
+
370
387
  if (isGroup) {
371
388
  preparedItem.groupItems = this.filterItemsForLabel(preparedItem.groupItems, str);
389
+
390
+ if (hasSearchQuery && preparedItem.groupItems.length > 0) {
391
+ item.collapsed = false;
392
+ }
393
+
372
394
  return preparedItem.groupItems.length > 0;
373
395
  }
396
+
374
397
  return this.checkAndUpdateLabel(item, str);
375
398
  });
376
399
  }
@@ -422,6 +445,34 @@ class UDExSearchableListBase extends UI5Element {
422
445
  }
423
446
  }
424
447
 
448
+ saveGroupCollapseState(): void {
449
+ this._groupCollapseStateBeforeSearch.clear();
450
+
451
+ this.items.forEach(item => {
452
+ if (item.isGroup) {
453
+ this._groupCollapseStateBeforeSearch.set(item._id, Boolean(item.collapsed));
454
+ }
455
+ });
456
+ }
457
+
458
+ restoreGroupCollapseState(): void {
459
+ if (!this._groupCollapseStateBeforeSearch.size) {
460
+ return;
461
+ }
462
+
463
+ this.items.forEach(item => {
464
+ if (item.isGroup) {
465
+ const collapsed = this._groupCollapseStateBeforeSearch.get(item._id);
466
+
467
+ if (collapsed !== undefined) {
468
+ item.collapsed = collapsed;
469
+ }
470
+ }
471
+ });
472
+
473
+ this._groupCollapseStateBeforeSearch.clear();
474
+ }
475
+
425
476
  getSlottedItems(items: Array<UDExListItemBase | UDExListGroupHeaderBase>): Array<UDExListItemBase | UDExListGroupHeaderBase> {
426
477
  const arr: Array<UDExListItemBase | UDExListGroupHeaderBase> = [];
427
478
  items.forEach(item => {
package/src/SelectBox.ts CHANGED
@@ -179,7 +179,6 @@ class UDExSelectBox extends UDExSelectBase {
179
179
  }
180
180
 
181
181
  handleHeaderPress(e:CustomEvent) {
182
- this._search.focus();
183
182
  this.onHeaderPress(e);
184
183
  }
185
184
 
package/src/TextField.ts CHANGED
@@ -357,7 +357,7 @@ class UDExTextField extends UI5Element {
357
357
  ariaDescribedBy?: string;
358
358
 
359
359
  /**
360
- * Defines the aria-has-popup attribute of the input.
360
+ * Defines the aria-haspopup attribute of the input.
361
361
  * @private
362
362
  */
363
363
  @property({ type: String })