@sapudex/web-components 2.26.0 → 2.28.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 (88) hide show
  1. package/config/dev-server.mjs +8 -0
  2. package/cypress/specs/RatingIndicator.cy.tsx +6 -5
  3. package/cypress/specs/TabNavigation.cy.tsx +1024 -0
  4. package/cypress/support/component-imports.src.ts +4 -0
  5. package/dist/RatingIndicator.d.ts +7 -0
  6. package/dist/RatingIndicator.js +10 -5
  7. package/dist/RatingIndicator.js.map +1 -1
  8. package/dist/RatingIndicatorTemplate.js +16 -4
  9. package/dist/RatingIndicatorTemplate.js.map +1 -1
  10. package/dist/Review.js +1 -1
  11. package/dist/Review.js.map +1 -1
  12. package/dist/SelectBase.d.ts +2 -0
  13. package/dist/SelectBase.js +6 -1
  14. package/dist/SelectBase.js.map +1 -1
  15. package/dist/Tab.d.ts +57 -0
  16. package/dist/Tab.js +67 -0
  17. package/dist/Tab.js.map +1 -1
  18. package/dist/TabNavigation.d.ts +25 -3
  19. package/dist/TabNavigation.js +96 -18
  20. package/dist/TabNavigation.js.map +1 -1
  21. package/dist/assets/{InputSuggestions-CG5owMFQ.js → InputSuggestions-DTdaDbps.js} +1 -1
  22. package/dist/assets/{ListItemStandardExpandableTextTemplate-DpVr7qdn.js → ListItemStandardExpandableTextTemplate-DoV7tVwi.js} +1 -1
  23. package/dist/assets/{bundle.esm-CTkLMr6f.js → bundle.esm-BUg2IPOZ.js} +218 -218
  24. package/dist/assets/messagebundle_en-DfoXjU7o.js +1 -0
  25. package/dist/css/themes/Checkbox.css +1 -1
  26. package/dist/css/themes/RatingIndicator.css +1 -1
  27. package/dist/css/themes/Review.css +1 -1
  28. package/dist/css/themes/ReviewItem.css +1 -1
  29. package/dist/css/themes/Slider.css +1 -1
  30. package/dist/css/themes/TabNavigation.css +1 -1
  31. package/dist/custom-elements-internal.json +68 -0
  32. package/dist/custom-elements.json +64 -0
  33. package/dist/generated/assets/i18n/messagebundle_en.json +1 -1
  34. package/dist/generated/i18n/i18n-defaults.d.ts +2 -1
  35. package/dist/generated/i18n/i18n-defaults.js +2 -1
  36. package/dist/generated/i18n/i18n-defaults.js.map +1 -1
  37. package/dist/generated/templates/TabHeaderTemplate.lit.js +30 -11
  38. package/dist/generated/templates/TabHeaderTemplate.lit.js.map +1 -1
  39. package/dist/generated/templates/TabNavigationTemplate.lit.js +8 -6
  40. package/dist/generated/templates/TabNavigationTemplate.lit.js.map +1 -1
  41. package/dist/generated/themes/Checkbox.css.d.ts +1 -1
  42. package/dist/generated/themes/Checkbox.css.js +1 -1
  43. package/dist/generated/themes/Checkbox.css.js.map +1 -1
  44. package/dist/generated/themes/RatingIndicator.css.d.ts +1 -1
  45. package/dist/generated/themes/RatingIndicator.css.js +1 -1
  46. package/dist/generated/themes/RatingIndicator.css.js.map +1 -1
  47. package/dist/generated/themes/Review.css.d.ts +1 -1
  48. package/dist/generated/themes/Review.css.js +1 -1
  49. package/dist/generated/themes/Review.css.js.map +1 -1
  50. package/dist/generated/themes/ReviewItem.css.d.ts +1 -1
  51. package/dist/generated/themes/ReviewItem.css.js +1 -1
  52. package/dist/generated/themes/ReviewItem.css.js.map +1 -1
  53. package/dist/generated/themes/Slider.css.d.ts +1 -1
  54. package/dist/generated/themes/Slider.css.js +1 -1
  55. package/dist/generated/themes/Slider.css.js.map +1 -1
  56. package/dist/generated/themes/TabNavigation.css.d.ts +1 -1
  57. package/dist/generated/themes/TabNavigation.css.js +1 -1
  58. package/dist/generated/themes/TabNavigation.css.js.map +1 -1
  59. package/dist/messagebundle.properties +1 -0
  60. package/dist/messagebundle_en.properties +1 -0
  61. package/dist/vscode.html-custom-data.json +16 -1
  62. package/dist/web-types.json +39 -2
  63. package/package.json +1 -1
  64. package/src/RatingIndicator.ts +18 -6
  65. package/src/RatingIndicatorTemplate.tsx +37 -9
  66. package/src/Review.ts +1 -1
  67. package/src/SelectBase.ts +8 -1
  68. package/src/Tab.ts +98 -0
  69. package/src/TabHeader.hbs +85 -33
  70. package/src/TabNavigation.hbs +16 -0
  71. package/src/TabNavigation.ts +109 -21
  72. package/src/generated/i18n/i18n-defaults.ts +2 -2
  73. package/src/generated/templates/TabHeaderTemplate.lit.ts +29 -10
  74. package/src/generated/templates/TabNavigationTemplate.lit.ts +8 -6
  75. package/src/generated/themes/Checkbox.css.ts +1 -1
  76. package/src/generated/themes/RatingIndicator.css.ts +1 -1
  77. package/src/generated/themes/Review.css.ts +1 -1
  78. package/src/generated/themes/ReviewItem.css.ts +1 -1
  79. package/src/generated/themes/Slider.css.ts +1 -1
  80. package/src/generated/themes/TabNavigation.css.ts +1 -1
  81. package/src/i18n/messagebundle.properties +1 -0
  82. package/src/i18n/messagebundle_en.properties +1 -0
  83. package/src/themes/Checkbox.css +9 -4
  84. package/src/themes/RatingIndicator.css +24 -9
  85. package/src/themes/Review.css +24 -31
  86. package/src/themes/ReviewItem.css +10 -11
  87. package/src/themes/TabNavigation.css +203 -1
  88. package/dist/assets/messagebundle_en-mjqY85LY.js +0 -1
@@ -2822,6 +2822,11 @@
2822
2822
  "name": "total-count",
2823
2823
  "description": "The number of total votes.",
2824
2824
  "values": []
2825
+ },
2826
+ {
2827
+ "name": "rating-count",
2828
+ "description": "The number of the rating.",
2829
+ "values": []
2825
2830
  }
2826
2831
  ],
2827
2832
  "references": []
@@ -3789,7 +3794,7 @@
3789
3794
  },
3790
3795
  {
3791
3796
  "name": "udex-tab",
3792
- "description": "<h3 class=\"comment-api-title\">Overview</h3>\n\n\n<h3>Usage</h3>\n\nFor the <code>udex-tab</code>\n<h3>ES6 Module Import</h3>\n\n<code>import \"@sapudex/web-components/dist/Tab.js\";</code>\n\n\n---\n\n\n\n\n### **Slots:**\n - **default** - Defines the content of the tab.",
3797
+ "description": "<h3 class=\"comment-api-title\">Overview</h3>\n\n\n<h3>Usage</h3>\n\nFor the <code>udex-tab</code>\n<h3>ES6 Module Import</h3>\n\n<code>import \"@sapudex/web-components/dist/Tab.js\";</code>\n\n\n---\n\n\n\n\n### **Slots:**\n - **default** - Defines the content of the tab.\n- **link** - Defines the link(s) to be displayed in the tab header (vertical orientation only).\nUse this slot to add one or more udex-link elements to the tab button.",
3793
3798
  "attributes": [
3794
3799
  {
3795
3800
  "name": "number-badge",
@@ -3801,6 +3806,11 @@
3801
3806
  "description": "Defines the text of tab.",
3802
3807
  "values": []
3803
3808
  },
3809
+ {
3810
+ "name": "body-text",
3811
+ "description": "Defines the body text (supplementary description) of the tab.\nOptional text to provide additional context or explanation.",
3812
+ "values": []
3813
+ },
3804
3814
  {
3805
3815
  "name": "accessible-name",
3806
3816
  "description": "Accessible name for tab.",
@@ -3853,6 +3863,11 @@
3853
3863
  "description": "Defines the mode of the component.",
3854
3864
  "values": [{ "name": "Light" }, { "name": "Dark" }]
3855
3865
  },
3866
+ {
3867
+ "name": "orientation",
3868
+ "description": "Defines the orientation of the tab navigation.",
3869
+ "values": [{ "name": "Vertical" }, { "name": "Horizontal" }]
3870
+ },
3856
3871
  {
3857
3872
  "name": "icon-without-background",
3858
3873
  "description": "Defines the background color of the component.",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
3
3
  "name": "@sapudex/web-components",
4
- "version": "2.26.0",
4
+ "version": "2.28.0",
5
5
  "description-markup": "markdown",
6
6
  "contributions": {
7
7
  "html": {
@@ -6212,6 +6212,11 @@
6212
6212
  "name": "total-count",
6213
6213
  "description": "The number of total votes.",
6214
6214
  "value": { "type": "number" }
6215
+ },
6216
+ {
6217
+ "name": "rating-count",
6218
+ "description": "The number of the rating.",
6219
+ "value": { "type": "number" }
6215
6220
  }
6216
6221
  ],
6217
6222
  "events": [],
@@ -6226,6 +6231,11 @@
6226
6231
  "name": "total-count",
6227
6232
  "description": "The number of total votes.",
6228
6233
  "value": { "type": "number" }
6234
+ },
6235
+ {
6236
+ "name": "rating-count",
6237
+ "description": "The number of the rating.",
6238
+ "value": { "type": "number" }
6229
6239
  }
6230
6240
  ],
6231
6241
  "events": []
@@ -8108,7 +8118,7 @@
8108
8118
  },
8109
8119
  {
8110
8120
  "name": "udex-tab",
8111
- "description": "<h3 class=\"comment-api-title\">Overview</h3>\n\n\n<h3>Usage</h3>\n\nFor the <code>udex-tab</code>\n<h3>ES6 Module Import</h3>\n\n<code>import \"@sapudex/web-components/dist/Tab.js\";</code>\n---\n\n\n### **Slots:**\n - **default** - Defines the content of the tab.",
8121
+ "description": "<h3 class=\"comment-api-title\">Overview</h3>\n\n\n<h3>Usage</h3>\n\nFor the <code>udex-tab</code>\n<h3>ES6 Module Import</h3>\n\n<code>import \"@sapudex/web-components/dist/Tab.js\";</code>\n---\n\n\n### **Slots:**\n - **default** - Defines the content of the tab.\n- **link** - Defines the link(s) to be displayed in the tab header (vertical orientation only).\nUse this slot to add one or more udex-link elements to the tab button.",
8112
8122
  "doc-url": "",
8113
8123
  "attributes": [
8114
8124
  {
@@ -8121,6 +8131,11 @@
8121
8131
  "description": "Defines the text of tab.",
8122
8132
  "value": { "type": "string" }
8123
8133
  },
8134
+ {
8135
+ "name": "body-text",
8136
+ "description": "Defines the body text (supplementary description) of the tab.\nOptional text to provide additional context or explanation.",
8137
+ "value": { "type": "string | undefined" }
8138
+ },
8124
8139
  {
8125
8140
  "name": "accessible-name",
8126
8141
  "description": "Accessible name for tab.",
@@ -8158,6 +8173,10 @@
8158
8173
  {
8159
8174
  "name": "default",
8160
8175
  "description": "Defines the content of the tab."
8176
+ },
8177
+ {
8178
+ "name": "link",
8179
+ "description": "Defines the link(s) to be displayed in the tab header (vertical orientation only).\nUse this slot to add one or more udex-link elements to the tab button."
8161
8180
  }
8162
8181
  ],
8163
8182
  "events": [],
@@ -8173,6 +8192,11 @@
8173
8192
  "description": "Defines the text of tab.",
8174
8193
  "value": { "type": "string" }
8175
8194
  },
8195
+ {
8196
+ "name": "body-text",
8197
+ "description": "Defines the body text (supplementary description) of the tab.\nOptional text to provide additional context or explanation.",
8198
+ "value": { "type": "string | undefined" }
8199
+ },
8176
8200
  {
8177
8201
  "name": "accessible-name",
8178
8202
  "description": "Accessible name for tab.",
@@ -8230,6 +8254,14 @@
8230
8254
  "default": "\"Light\""
8231
8255
  }
8232
8256
  },
8257
+ {
8258
+ "name": "orientation",
8259
+ "description": "Defines the orientation of the tab navigation.",
8260
+ "value": {
8261
+ "type": "\"Vertical\" | \"Horizontal\"",
8262
+ "default": "\"Horizontal\""
8263
+ }
8264
+ },
8233
8265
  {
8234
8266
  "name": "icon-without-background",
8235
8267
  "description": "Defines the background color of the component.",
@@ -8264,6 +8296,11 @@
8264
8296
  "description": "Defines the mode of the component.",
8265
8297
  "value": { "type": "\"Light\" | \"Dark\"" }
8266
8298
  },
8299
+ {
8300
+ "name": "orientation",
8301
+ "description": "Defines the orientation of the tab navigation.",
8302
+ "value": { "type": "\"Vertical\" | \"Horizontal\"" }
8303
+ },
8267
8304
  {
8268
8305
  "name": "icon-without-background",
8269
8306
  "description": "Defines the background color of the component.",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@sapudex/web-components",
4
- "version": "2.26.0",
4
+ "version": "2.28.0",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
7
7
  },
@@ -11,7 +11,7 @@ import RatingIndicatorTemplate from "./RatingIndicatorTemplate.js";
11
11
  import RatingIndicatorCss from "./generated/themes/RatingIndicator.css.js";
12
12
  import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js";
13
13
  import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
14
- import { TOTAL } from "./generated/i18n/i18n-defaults.js";
14
+ import { TOTAL, RATING_COUNT_TITLE } from "./generated/i18n/i18n-defaults.js";
15
15
  import translate from "./utils/translate.js";
16
16
  import "@ui5/webcomponents-icons/dist/favorite.js";
17
17
  import "@ui5/webcomponents-icons/dist/unfavorite.js";
@@ -59,20 +59,32 @@ class UDExRatingIndicator extends RatingIndicator {
59
59
  @property()
60
60
  totalCount!: number;
61
61
 
62
+ /**
63
+ * The number of the rating.
64
+ *
65
+ * @public
66
+ */
67
+ @property()
68
+ ratingCount!: number;
69
+
62
70
  @i18n("@sapudex/web-components")
63
71
  static i18nBundle: I18nBundle;
64
72
 
65
73
  get _ariaLabel(): string {
66
- const ariaLabelText: string = getEffectiveAriaLabelText(this) || "";
67
- if (this.totalCount) {
68
- return `${ariaLabelText ? `${ariaLabelText}, ` : ""}${translate(RatingIndicator, TOTAL)} ${this.totalCount}`;
69
- }
70
- return ariaLabelText;
74
+ const ariaLabelText = getEffectiveAriaLabelText(this) || "";
75
+ const ratingText = this.ratingCount ? `${translate(RatingIndicator, RATING_COUNT_TITLE)} ${this.ratingCount}` : "";
76
+ const totalText = this.totalCount ? `${translate(RatingIndicator, TOTAL)} ${this.totalCount}` : "";
77
+
78
+ return [ariaLabelText, ratingText, totalText].filter(Boolean).join(", ");
71
79
  }
72
80
 
73
81
  get hasTotalCount(): boolean {
74
82
  return !!this.totalCount || this.totalCount === 0;
75
83
  }
84
+
85
+ get hasRatingCount(): boolean {
86
+ return !!this.ratingCount || this.ratingCount === 0;
87
+ }
76
88
  }
77
89
 
78
90
  UDExRatingIndicator.define();
@@ -1,6 +1,12 @@
1
1
  import type UDExRatingIndicator from "./RatingIndicator.js";
2
2
  import UI5Icon from "@ui5/webcomponents/dist/Icon.js";
3
3
 
4
+ type Star = {
5
+ selected: boolean;
6
+ index: number;
7
+ halfStar: boolean;
8
+ };
9
+
4
10
  export default function RatingIndicatorTemplate(this: UDExRatingIndicator) {
5
11
  const rootClass = "udex-rating-indicator";
6
12
  const ui5Class = "ui5-rating-indicator";
@@ -32,22 +38,44 @@ export default function RatingIndicatorTemplate(this: UDExRatingIndicator) {
32
38
  "udex-rating-indicator__item ui5-rating-indicator-item": true,
33
39
  "udex-rating-indicator__item-sel ui5-rating-indicator-item-sel": star.selected,
34
40
  "udex-rating-indicator__item-unsel ui5-rating-indicator-item-unsel": !star.selected && !star.halfStar,
35
- "udex-rating-indicator__item-half ui5-rating-indicator-item-half": star.halfStar && !star.selected,
41
+ "udex-rating-indicator__item-half ui5-rating-indicator-item-half udex-rating-indicator__item-unsel": star.halfStar && !star.selected,
36
42
  }}>
37
- <UI5Icon data-ui5-value={star.index} name={star.selected ? "favorite" : "unfavorite"}></UI5Icon>
38
- {star.halfStar && (
39
- <div class={`${rootClass}__half-icon-wrapper ${ui5Class}-half-icon-wrapper`}>
40
- <UI5Icon data-ui5-value={star.index} name="favorite" class={`${rootClass}__half-icon ${ui5Class}-half-icon`}></UI5Icon>
41
- </div>
42
- )}
43
+ {starRender.call(this, star, ui5Class, rootClass)}
43
44
  </li>
44
45
  ))}
45
46
  </ul>
46
- {this.hasTotalCount && (
47
+ {(this.hasRatingCount || this.hasTotalCount) && (
47
48
  <div class={`${rootClass}__total`}>
48
- &#40;{this.totalCount}&#41;
49
+ {this.hasRatingCount && <>{this.ratingCount} </>}
50
+ {this.hasTotalCount && <>&#40;{this.totalCount}&#41;</>}
49
51
  </div>
50
52
  )}
51
53
  </div>
52
54
  );
53
55
  }
56
+
57
+ function starRender(this: UDExRatingIndicator, star: Star, ui5Class: string, rootClass: string) {
58
+ if (star.selected) {
59
+ return (
60
+ <UI5Icon data-ui5-value={star.index} name="favorite"/>
61
+ );
62
+ }
63
+ if (star.halfStar) {
64
+ return (
65
+ <>
66
+ <UI5Icon data-ui5-value={star.index} name={this.readonly ? "favorite" : "unfavorite"}/>
67
+ <div class={`${rootClass}__half-icon-wrapper ${ui5Class}-half-icon-wrapper`}>
68
+ <UI5Icon data-ui5-value={star.index} name="favorite" class={`${rootClass}__half-icon ${ui5Class}-half-icon`}></UI5Icon>
69
+ </div>
70
+ </>
71
+ );
72
+ }
73
+ if (this.readonly) {
74
+ return (
75
+ <UI5Icon data-ui5-value={star.index} name="favorite"/>
76
+ );
77
+ }
78
+ return (
79
+ <UI5Icon data-ui5-value={star.index} name="unfavorite"/>
80
+ );
81
+ }
package/src/Review.ts CHANGED
@@ -10,10 +10,10 @@ import "@ui5/webcomponents-icons/dist/favorite.js";
10
10
  import type UDExRatingBar from "./RatingBar.js";
11
11
  import type UDExButton from "./Button.js";
12
12
  import ReviewTemplate from "./ReviewTemplate.js";
13
+ import translate from "./utils/translate.js";
13
14
 
14
15
  // Styles
15
16
  import ReviewCss from "./generated/themes/Review.css.js";
16
- import translate from "./utils/translate.js";
17
17
 
18
18
  /**
19
19
  * @class
package/src/SelectBase.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import property from "@ui5/webcomponents-base/dist/decorators/property.js";
2
2
  import executeTemplate from "@ui5/webcomponents-base/dist/renderer/executeTemplate.js";
3
+ import type { ChangeInfo } from "@ui5/webcomponents-base/dist/UI5Element.js";
3
4
  import {
4
5
  isEscape, isDown, isUp, isHome, isEnd, isTabNext, isTabPrevious,
5
6
  } from "@ui5/webcomponents-base/dist/Keys.js";
@@ -183,7 +184,7 @@ class UDExSelectBase extends UDExSearchableListBase {
183
184
 
184
185
  onEnterDOM() {
185
186
  ResizeHandler.register(this, this._handleResizeBound);
186
- if (this.modeSingleSelect) {
187
+ if (this.modeSingleSelect && this.hasSelectedItems) {
187
188
  this.setSelectOption();
188
189
  }
189
190
  }
@@ -192,6 +193,12 @@ class UDExSelectBase extends UDExSearchableListBase {
192
193
  ResizeHandler.deregister(this, this._handleResizeBound);
193
194
  }
194
195
 
196
+ onInvalidation({ name }: ChangeInfo): void {
197
+ if (name === "default") {
198
+ this.setSelectOption();
199
+ }
200
+ }
201
+
195
202
  onAfterRendering() {
196
203
  this._popover = this.getPopover();
197
204
  this._search = this.getSearch();
package/src/Tab.ts CHANGED
@@ -25,14 +25,18 @@ export interface ITab extends UI5Element {
25
25
  selected?: boolean;
26
26
  numberBadge?: string;
27
27
  text?: string;
28
+ bodyText?: string;
28
29
  showAttentionBadge?: boolean;
29
30
  accessibleName?: string;
30
31
  href?: string;
31
32
  content?: Array<Node>;
33
+ link?: Array<HTMLElement>;
32
34
  hasIcon: boolean;
35
+ isMobile: boolean;
33
36
  isOnSelectedTab: boolean;
34
37
  hasNotIcon: boolean;
35
38
  hasNotHref: boolean;
39
+ hasLinkSlot: boolean;
36
40
  selectedClass: string;
37
41
  tabNavigation: any;
38
42
  _individualSlot: string;
@@ -40,6 +44,8 @@ export interface ITab extends UI5Element {
40
44
  _slotName: string;
41
45
  _selected?: boolean;
42
46
  _tabIndex: string;
47
+ _isMobile: boolean;
48
+ _isVertical: boolean;
43
49
  _iconWithoutBackground: boolean;
44
50
  _posinset?: number;
45
51
  _setsize?: number;
@@ -91,6 +97,16 @@ class UDExTab extends UI5Element implements ITab {
91
97
  */
92
98
  @property({ type: String })
93
99
  text!: string;
100
+
101
+ /**
102
+ * Defines the body text (supplementary description) of the tab.
103
+ * Optional text to provide additional context or explanation.
104
+ *
105
+ * @public
106
+ */
107
+ @property({ type: String })
108
+ bodyText?: string;
109
+
94
110
  /**
95
111
  * Accessible name for tab.
96
112
  *
@@ -167,6 +183,32 @@ class UDExTab extends UI5Element implements ITab {
167
183
  @property({ type: String })
168
184
  _tabIndex!: string;
169
185
 
186
+ /**
187
+ * Defines if the viewport is mobile size.
188
+ *
189
+ * @private
190
+ * @default false
191
+ */
192
+ @property({ type: Boolean, noAttribute: true })
193
+ _isMobile: boolean = false;
194
+
195
+ /**
196
+ * Defines if the tab navigation is in vertical orientation.
197
+ *
198
+ * @private
199
+ * @default false
200
+ */
201
+ @property({ type: Boolean, noAttribute: true })
202
+ _isVertical: boolean = false;
203
+
204
+ /**
205
+ * Defines the width of the tab.
206
+ *
207
+ * @private
208
+ */
209
+ @property({ type: String, noAttribute: true })
210
+ _width?: string;
211
+
170
212
  /**
171
213
  * Defines the background color of the component.
172
214
  *
@@ -185,6 +227,16 @@ class UDExTab extends UI5Element implements ITab {
185
227
  @slot({ type: Node, "default": true })
186
228
  content!: Array<Node>;
187
229
 
230
+ /**
231
+ * Defines the link(s) to be displayed in the tab header (vertical orientation only).
232
+ * Use this slot to add one or more udex-link elements to the tab button.
233
+ *
234
+ * @slot link
235
+ * @public
236
+ */
237
+ @slot({ type: HTMLElement })
238
+ link!: Array<HTMLElement>;
239
+
188
240
  @i18n("@sapudex/web-components")
189
241
  static i18nBundle: I18nBundle;
190
242
 
@@ -219,6 +271,14 @@ class UDExTab extends UI5Element implements ITab {
219
271
  return this.hasIcon ? " udex-tab-navigation__button-tab--icon" : "";
220
272
  }
221
273
 
274
+ get isMobile(): boolean {
275
+ return this._isMobile;
276
+ }
277
+
278
+ get isMobileVertical(): boolean {
279
+ return this._isMobile && this._isVertical;
280
+ }
281
+
222
282
  get hasNotIcon(): boolean {
223
283
  return !this.icon && this.showAttentionBadge;
224
284
  }
@@ -227,6 +287,37 @@ class UDExTab extends UI5Element implements ITab {
227
287
  return !this.href;
228
288
  }
229
289
 
290
+ get hasBodyText(): boolean {
291
+ return !!this.bodyText;
292
+ }
293
+
294
+ /**
295
+ * Returns true if aria-describedby should point to bodyText.
296
+ * Only applies to selected tabs with bodyText in vertical orientation.
297
+ */
298
+ get ariaDescribedByBodyText(): boolean {
299
+ return this.effectiveSelected && this.hasBodyText && this._isVertical;
300
+ }
301
+
302
+ get hasLinkSlot(): boolean {
303
+ return this.link && this.link.length > 0;
304
+ }
305
+
306
+ /**
307
+ * Returns the HTML content of the link slot elements.
308
+ * This is used to render the links in the TabHeader template.
309
+ */
310
+ get linkSlotHtml(): string {
311
+ if (!this.link || this.link.length === 0) {
312
+ return "";
313
+ }
314
+ return this.link.map(l => l.outerHTML).join("");
315
+ }
316
+
317
+ get hasBodyTextOrLink(): boolean {
318
+ return this._isVertical && (this.hasBodyText || this.hasLinkSlot);
319
+ }
320
+
230
321
  get effectiveSelected(): boolean {
231
322
  return this.selected || this._selected;
232
323
  }
@@ -265,6 +356,13 @@ class UDExTab extends UI5Element implements ITab {
265
356
  return this.href && this.effectiveSelected ? "1" : "-1";
266
357
  }
267
358
 
359
+ get widthStyle(): string {
360
+ if (this._width && this._width !== "auto") {
361
+ return `width: ${this._width};`;
362
+ }
363
+ return "";
364
+ }
365
+
268
366
  get tabNavigation() {
269
367
  return executeTemplate(UDExTab.headerTemplate, this);
270
368
  }
package/src/TabHeader.hbs CHANGED
@@ -1,34 +1,81 @@
1
- {{#if hasNotHref}}
2
- <button
3
- id="{{this._id}}"
4
- class="udex-tab-navigation__button-tab {{selectedClass}} {{classWithIconWithoutBackground}} {{buttonClassWithIcon}}"
5
- tabindex="{{this._tabIndex}}"
6
- type="button"
7
- role="tab"
8
- aria-describedby="udex-tab-navigation__{{this._id}}__accessible-name"
9
- aria-posinset="{{this._posinset}}"
10
- aria-setsize="{{this._setsize}}"
11
- aria-controls="{{this._contentId}}"
12
- aria-selected="{{this.effectiveSelected}}"
13
- aria-labelledby="{{this.ariaLabelledBy}}"
1
+ {{#if hasBodyTextOrLink}}
2
+ <div class="udex-tab-navigation__tab-wrapper"
3
+ role="presentation"
14
4
  ._realTab="{{this}}"
15
- style="width: {{this._width}};">
16
- {{>tab-content}}
17
- </button>
5
+ {{#if this.widthStyle}}style="{{this.widthStyle}}"{{/if}}>
6
+ {{#if hasNotHref}}
7
+ <button
8
+ id="{{this._id}}"
9
+ class="udex-tab-navigation__button-tab {{selectedClass}} {{classWithIconWithoutBackground}} {{buttonClassWithIcon}}"
10
+ type="button"
11
+ role="tab"
12
+ tabindex="{{this._tabIndex}}"
13
+ aria-posinset="{{this._posinset}}"
14
+ aria-setsize="{{this._setsize}}"
15
+ aria-controls="{{this._contentId}}"
16
+ aria-selected="{{this.effectiveSelected}}">
17
+ {{>tab-content}}
18
+ {{#if this.ariaDescribedByBodyText}}
19
+ <span class="visually-hidden">{{bodyText}}</span>
20
+ {{/if}}
21
+ </button>
22
+ {{else}}
23
+ <a
24
+ id="{{this._id}}"
25
+ class="udex-tab-navigation__button-tab {{selectedClass}} {{classWithIconWithoutBackground}} {{buttonClassWithIcon}}"
26
+ href="{{href}}"
27
+ target="{{target}}"
28
+ role="tab"
29
+ tabindex="{{this.linkTabIndex}}"
30
+ aria-posinset="{{this._posinset}}"
31
+ aria-setsize="{{this._setsize}}">
32
+ {{>tab-content}}
33
+ {{#if this.ariaDescribedByBodyText}}
34
+ <span class="visually-hidden">{{bodyText}}</span>
35
+ {{/if}}
36
+ </a>
37
+ {{/if}}
38
+ {{#if hasBodyText}}
39
+ <p class="udex-tab-navigation__button-body-text">{{bodyText}}</p>
40
+ {{/if}}
41
+ {{#if hasLinkSlot}}
42
+ <div class="udex-tab-navigation__button-links">
43
+ {{{linkSlotHtml}}}
44
+ </div>
45
+ {{/if}}
46
+ </div>
18
47
  {{else}}
19
- <a
20
- id="{{this._id}}"
21
- class="udex-tab-navigation__button-tab {{selectedClass}} {{classWithIconWithoutBackground}} {{buttonClassWithIcon}}"
22
- tabindex="{{this.linkTabIndex}}"
23
- role="tab"
24
- aria-posinset="{{this._posinset}}"
25
- aria-setsize="{{this._setsize}}"
26
- href="{{href}}"
27
- target="{{target}}"
28
- style="max-width: {{this._maxWidth}}px; width: {{this._width}};"
29
- ._realTab="{{this}}">
30
- {{>tab-content}}
31
- </a>
48
+ {{#if hasNotHref}}
49
+ <button
50
+ id="{{this._id}}"
51
+ class="udex-tab-navigation__button-tab {{selectedClass}} {{classWithIconWithoutBackground}} {{buttonClassWithIcon}}"
52
+ tabindex="{{this._tabIndex}}"
53
+ type="button"
54
+ role="tab"
55
+ aria-describedby="udex-tab-navigation__{{this._id}}__accessible-name"
56
+ aria-posinset="{{this._posinset}}"
57
+ aria-setsize="{{this._setsize}}"
58
+ aria-controls="{{this._contentId}}"
59
+ aria-selected="{{this.effectiveSelected}}"
60
+ ._realTab="{{this}}"
61
+ {{#if this.widthStyle}}style="{{this.widthStyle}}"{{/if}}>
62
+ {{>tab-content}}
63
+ </button>
64
+ {{else}}
65
+ <a
66
+ id="{{this._id}}"
67
+ class="udex-tab-navigation__button-tab {{selectedClass}} {{classWithIconWithoutBackground}} {{buttonClassWithIcon}}"
68
+ tabindex="{{this.linkTabIndex}}"
69
+ role="tab"
70
+ aria-posinset="{{this._posinset}}"
71
+ aria-setsize="{{this._setsize}}"
72
+ href="{{href}}"
73
+ target="{{target}}"
74
+ {{#if this.widthStyle}}style="{{this.widthStyle}}"{{/if}}
75
+ ._realTab="{{this}}">
76
+ {{>tab-content}}
77
+ </a>
78
+ {{/if}}
32
79
  {{/if}}
33
80
 
34
81
  {{#*inline "tab-content"}}
@@ -56,9 +103,14 @@
56
103
  {{numberBadge}}
57
104
  </udex-badge>
58
105
  {{/if}}
106
+ {{#if hasNotIcon}}
107
+ <span class="udex-tab-navigation__button-badge"></span>
108
+ {{/if}}
59
109
  </p>
60
- {{#if hasNotIcon}}
61
- <span class="udex-tab-navigation__button-badge"></span>
62
- {{/if}}
63
- <span aria-hidden="true" class="hidden" id="udex-tab-navigation__{{this._id}}__accessible-name">{{accessibleNameText}}</span>
110
+ <span
111
+ class="visually-hidden"
112
+ id="udex-tab-navigation__{{this._id}}__accessible-name"
113
+ >
114
+ {{accessibleNameText}}
115
+ </span>
64
116
  {{/inline}}
@@ -9,10 +9,25 @@
9
9
  class="udex-tab-navigation__header-tabs"
10
10
  role="tablist"
11
11
  aria-labelledby="tablist-{{_id}}"
12
+ aria-orientation="{{ariaOrientationValue}}"
12
13
  part="tab-navigation-header-tabs"
13
14
  @click="{{onTabClick}}">
14
15
  {{#each items}}
15
16
  {{this.tabNavigation}}
17
+ {{#if this.isMobileVertical}}
18
+ {{#if this.selected}}
19
+ <div
20
+ class="udex-tab-navigation__content"
21
+ part="tab-navigation-content">
22
+ <div
23
+ class="udex-tab-navigation__content-item"
24
+ id="{{_contentId}}-mobile"
25
+ aria-labelledby="{{_id}}">
26
+ <slot name="{{this._slotName}}"></slot>
27
+ </div>
28
+ </div>
29
+ {{/if}}
30
+ {{/if}}
16
31
  {{/each}}
17
32
  </div>
18
33
  <button
@@ -44,6 +59,7 @@
44
59
  <div
45
60
  class="udex-tab-navigation__content-item"
46
61
  id="{{_contentId}}"
62
+ aria-hidden="{{this.IsMobile}}"
47
63
  aria-labelledby="{{_id}}">
48
64
  <slot name="{{this._slotName}}"></slot>
49
65
  </div>