@sapui5/sap.fe.templates 1.141.1 → 1.142.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 (74) hide show
  1. package/package.json +1 -1
  2. package/src/sap/fe/templates/.library +1 -1
  3. package/src/sap/fe/templates/AnalyticalListPage/manifest.json +1 -1
  4. package/src/sap/fe/templates/ListReport/ExtensionAPI.js +1 -1
  5. package/src/sap/fe/templates/ListReport/ExtensionAPI.ts +1 -1
  6. package/src/sap/fe/templates/ListReport/ListReport.view.xml +10 -12
  7. package/src/sap/fe/templates/ListReport/ListReportController.controller.js +95 -60
  8. package/src/sap/fe/templates/ListReport/ListReportController.controller.ts +92 -53
  9. package/src/sap/fe/templates/ListReport/manifest.json +1 -1
  10. package/src/sap/fe/templates/ListReport/overrides/MessageHandler.js +25 -0
  11. package/src/sap/fe/templates/ListReport/overrides/MessageHandler.ts +20 -0
  12. package/src/sap/fe/templates/ObjectPage/ExtensionAPI.js +180 -13
  13. package/src/sap/fe/templates/ObjectPage/ExtensionAPI.ts +209 -13
  14. package/src/sap/fe/templates/ObjectPage/ObjectPage.view.xml +4 -0
  15. package/src/sap/fe/templates/ObjectPage/ObjectPageController.controller.js +136 -24
  16. package/src/sap/fe/templates/ObjectPage/ObjectPageController.controller.ts +178 -26
  17. package/src/sap/fe/templates/ObjectPage/ObjectPageTemplating.js +3 -2
  18. package/src/sap/fe/templates/ObjectPage/ObjectPageTemplating.ts +2 -1
  19. package/src/sap/fe/templates/ObjectPage/manifest.json +1 -1
  20. package/src/sap/fe/templates/ObjectPage/view/fragments/Actions.fragment.xml +7 -4
  21. package/src/sap/fe/templates/ObjectPage/view/fragments/Heading.fragment.xml +2 -2
  22. package/src/sap/fe/templates/library.js +1 -1
  23. package/src/sap/fe/templates/messagebundle.properties +8 -7
  24. package/src/sap/fe/templates/messagebundle_ar.properties +5 -5
  25. package/src/sap/fe/templates/messagebundle_bg.properties +6 -6
  26. package/src/sap/fe/templates/messagebundle_ca.properties +4 -4
  27. package/src/sap/fe/templates/messagebundle_cnr.properties +5 -5
  28. package/src/sap/fe/templates/messagebundle_cs.properties +6 -6
  29. package/src/sap/fe/templates/messagebundle_cy.properties +5 -5
  30. package/src/sap/fe/templates/messagebundle_da.properties +6 -6
  31. package/src/sap/fe/templates/messagebundle_de.properties +7 -7
  32. package/src/sap/fe/templates/messagebundle_el.properties +5 -5
  33. package/src/sap/fe/templates/messagebundle_en.properties +6 -6
  34. package/src/sap/fe/templates/messagebundle_en_GB.properties +6 -6
  35. package/src/sap/fe/templates/messagebundle_en_US_saprigi.properties +3 -3
  36. package/src/sap/fe/templates/messagebundle_es.properties +4 -4
  37. package/src/sap/fe/templates/messagebundle_es_MX.properties +4 -4
  38. package/src/sap/fe/templates/messagebundle_et.properties +4 -4
  39. package/src/sap/fe/templates/messagebundle_fi.properties +4 -4
  40. package/src/sap/fe/templates/messagebundle_fr.properties +4 -4
  41. package/src/sap/fe/templates/messagebundle_fr_CA.properties +5 -5
  42. package/src/sap/fe/templates/messagebundle_hi.properties +6 -6
  43. package/src/sap/fe/templates/messagebundle_hr.properties +4 -4
  44. package/src/sap/fe/templates/messagebundle_hu.properties +6 -6
  45. package/src/sap/fe/templates/messagebundle_id.properties +7 -7
  46. package/src/sap/fe/templates/messagebundle_it.properties +4 -4
  47. package/src/sap/fe/templates/messagebundle_iw.properties +6 -6
  48. package/src/sap/fe/templates/messagebundle_ja.properties +4 -4
  49. package/src/sap/fe/templates/messagebundle_kk.properties +4 -4
  50. package/src/sap/fe/templates/messagebundle_ko.properties +4 -4
  51. package/src/sap/fe/templates/messagebundle_lt.properties +4 -4
  52. package/src/sap/fe/templates/messagebundle_lv.properties +6 -6
  53. package/src/sap/fe/templates/messagebundle_mk.properties +4 -4
  54. package/src/sap/fe/templates/messagebundle_ms.properties +5 -5
  55. package/src/sap/fe/templates/messagebundle_nl.properties +4 -4
  56. package/src/sap/fe/templates/messagebundle_no.properties +4 -4
  57. package/src/sap/fe/templates/messagebundle_pl.properties +4 -4
  58. package/src/sap/fe/templates/messagebundle_pt.properties +5 -5
  59. package/src/sap/fe/templates/messagebundle_pt_PT.properties +6 -6
  60. package/src/sap/fe/templates/messagebundle_ro.properties +4 -4
  61. package/src/sap/fe/templates/messagebundle_ru.properties +4 -4
  62. package/src/sap/fe/templates/messagebundle_sh.properties +5 -5
  63. package/src/sap/fe/templates/messagebundle_sk.properties +4 -4
  64. package/src/sap/fe/templates/messagebundle_sl.properties +6 -6
  65. package/src/sap/fe/templates/messagebundle_sr.properties +5 -5
  66. package/src/sap/fe/templates/messagebundle_sv.properties +4 -4
  67. package/src/sap/fe/templates/messagebundle_th.properties +4 -4
  68. package/src/sap/fe/templates/messagebundle_tr.properties +4 -4
  69. package/src/sap/fe/templates/messagebundle_uk.properties +4 -4
  70. package/src/sap/fe/templates/messagebundle_vi.properties +6 -6
  71. package/src/sap/fe/templates/messagebundle_zh_CN.properties +4 -4
  72. package/src/sap/fe/templates/messagebundle_zh_TW.properties +4 -4
  73. package/src/sap/fe/templates/types/sap.fe.templates-auto-ext.d.js +10 -0
  74. package/src/sap/fe/templates/types/sap.fe.templates-auto-ext.d.ts +49 -0
@@ -1,4 +1,5 @@
1
1
  import Log from "sap/base/Log";
2
+ import uid from "sap/base/util/uid";
2
3
  import { defineUI5Class } from "sap/fe/base/ClassSupport";
3
4
  import CommonUtils from "sap/fe/core/CommonUtils";
4
5
  import ExtensionAPI from "sap/fe/core/ExtensionAPI";
@@ -10,6 +11,11 @@ import ResourceModelHelper from "sap/fe/core/helpers/ResourceModelHelper";
10
11
  import type { SideEffectsTargetType } from "sap/fe/core/services/SideEffectsServiceFactory";
11
12
  import type Section from "sap/fe/macros/controls/Section";
12
13
  import type TableAPI from "sap/fe/macros/table/TableAPI";
14
+ import Link from "sap/m/Link";
15
+ import MessageItem from "sap/m/MessageItem";
16
+ import MessagePopover from "sap/m/MessagePopover";
17
+ import MessageStrip from "sap/m/MessageStrip";
18
+ import type UI5Event from "sap/ui/base/Event";
13
19
  import type ManagedObject from "sap/ui/base/ManagedObject";
14
20
  import InvisibleMessage from "sap/ui/core/InvisibleMessage";
15
21
  import { InvisibleMessageMode } from "sap/ui/core/library";
@@ -20,6 +26,8 @@ import type Control from "sap/ui/mdc/Control";
20
26
  import type Table from "sap/ui/mdc/Table";
21
27
  import type Context from "sap/ui/model/Context";
22
28
  import type ODataV4Context from "sap/ui/model/odata/v4/Context";
29
+ import type ObjectPageDynamicHeaderTitle from "sap/uxap/ObjectPageDynamicHeaderTitle";
30
+ import type ObjectPageLayout from "sap/uxap/ObjectPageLayout";
23
31
 
24
32
  /**
25
33
  * Extension API for object pages on SAP Fiori elements for OData V4.
@@ -32,11 +40,16 @@ import type ODataV4Context from "sap/ui/model/odata/v4/Context";
32
40
  */
33
41
  @defineUI5Class("sap.fe.templates.ObjectPage.ExtensionAPI")
34
42
  class ObjectPageExtensionAPI extends ExtensionAPI {
43
+ /**
44
+ * ID of the active custom MessageStrip for programmatic removal.
45
+ */
46
+ private customMessageStripId?: string;
47
+
35
48
  /**
36
49
  * Refreshes either the whole object page or only parts of it.
37
50
  * @param [vPath] Path or array of paths referring to entities or properties to be refreshed.
38
51
  * If omitted, the whole object page is refreshed. The path "" refreshes the entity assigned to the object page
39
- * without navigations
52
+ * without navigation.
40
53
  * @returns Resolved once the data is refreshed or rejected if the request failed
41
54
  * @public
42
55
  */
@@ -141,23 +154,164 @@ class ObjectPageExtensionAPI extends ExtensionAPI {
141
154
  }
142
155
 
143
156
  /**
144
- * Displays the message strip between the title and the header of the ObjectPage.
145
- * @param messages The message to be displayed
157
+ * Displays the message strip between the title and the header of the ObjectPage. If only one message is provided, it is displayed directly in the message strip. If multiple messages are provided, they are prioritized by the order: Error, Warning, and Information and a corresponding generic text is displayed:
158
+ * - Error: "The object contains errors."
159
+ * - Warning: "The object contains warnings."
160
+ * - Information: "The object contains messages."
161
+ * If a back-end message is received with a target pointing to the Object Page, it is displayed in the message strip, overriding any existing message or message strip. If multiple back-end messages are received, the message with the highest priority is displayed with a generic text as described above.
162
+ * @param messagesOrStrip The message to be displayed or a MessageStrip control
146
163
  * @public
147
164
  */
165
+ showMessages(messagesOrStrip: Message[] | MessageStrip): void {
166
+ if (Array.isArray(messagesOrStrip)) {
167
+ this._showMessages(messagesOrStrip);
168
+ } else if (messagesOrStrip.isA("sap.m.MessageStrip")) {
169
+ this.showCustomMessageStrip(messagesOrStrip);
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Shows a custom MessageStrip control provided by the application.
175
+ * Ensures mutual exclusivity with the MessageStrip rendered by the object page view.
176
+ * @param messageStrip MessageStrip control
177
+ * @private
178
+ */
179
+ private showCustomMessageStrip(messageStrip: MessageStrip): void {
180
+ const messageStripInternalModelContext = this._view
181
+ .getModel("internal")
182
+ .bindContext(this._getMessageStripBindingContextPath())
183
+ .getBoundContext();
184
+ if (!messageStripInternalModelContext) {
185
+ return;
186
+ }
187
+
188
+ this.removeCustomMessageStrip(); // Remove any existing custom message strip before adding new one
189
+
190
+ // Hide framework generated message strip to ensure mutual exclusivity
191
+ messageStripInternalModelContext.setProperty("OPBackendMessageVisible", false);
192
+ messageStripInternalModelContext.setProperty("OPCustomMessageVisible", false);
193
+
194
+ this.customMessageStripId = messageStrip.getId() || uid(); // Store reference for programmatic removal
195
+
196
+ messageStrip.addStyleClass("sapUiSmallMarginTop");
197
+ this.addCustomMessageStripToPage(messageStrip, messageStripInternalModelContext);
198
+ }
199
+
200
+ /**
201
+ * Adds a custom MessageStrip to the page layout in the header container such as in expanded and snapped content.
202
+ * @param messageStrip The MessageStrip control to add
203
+ * @param messagestripInternalModelContext
204
+ */
205
+ private addCustomMessageStripToPage(messageStrip: MessageStrip, messagestripInternalModelContext: Context): void {
206
+ const headerContainer = this._view.byId("fe::ObjectPageDynamicHeaderTitle");
207
+ if (headerContainer) {
208
+ const insertIntoAggregation = (aggregationName: string, controlToInsert: MessageStrip): void => {
209
+ const aggregationContent = headerContainer.getAggregation(aggregationName);
210
+ if (aggregationContent) {
211
+ const contentArray = Array.isArray(aggregationContent) ? aggregationContent : [aggregationContent];
212
+ headerContainer.insertAggregation(aggregationName, controlToInsert, contentArray.length);
213
+ }
214
+ };
215
+ insertIntoAggregation("expandedContent", messageStrip);
216
+ insertIntoAggregation("snappedContent", messageStrip.clone());
217
+
218
+ messagestripInternalModelContext.setProperty("OPCustomMessageStripVisible", true);
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Removes the active custom MessageStrip programmatically.
224
+ */
225
+ private removeCustomMessageStrip(): void {
226
+ if (!this.customMessageStripId) {
227
+ return;
228
+ }
229
+
230
+ // Find and remove the custom message strip from header container
231
+ const headerContainer = this._view.byId("fe::ObjectPageDynamicHeaderTitle");
232
+ if (headerContainer) {
233
+ const removeFromAggregation = (aggregationName: string, idMatcher: (controlId: string) => boolean): void => {
234
+ const aggregationContent = headerContainer.getAggregation(aggregationName) as Control[];
235
+ if (aggregationContent) {
236
+ const controlToRemove = aggregationContent.find((control) => idMatcher(control.getId()));
237
+ if (controlToRemove) {
238
+ headerContainer.removeAggregation(aggregationName, controlToRemove);
239
+ controlToRemove.destroy();
240
+ }
241
+ }
242
+ };
243
+ removeFromAggregation("expandedContent", (controlId) => controlId === this.customMessageStripId);
244
+ removeFromAggregation("snappedContent", (controlId) => controlId.includes(this.customMessageStripId + "-__clone"));
245
+ }
246
+
247
+ this.customMessageStripId = undefined;
248
+
249
+ const messagestripInternalModelContext = this._view
250
+ .getModel("internal")
251
+ .bindContext(this._getMessageStripBindingContextPath())
252
+ .getBoundContext();
253
+ if (messagestripInternalModelContext) {
254
+ messagestripInternalModelContext.setProperty("OPCustomMessageStripVisible", false);
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Creates a MessageItem from a Message instance.
260
+ * @param message The message object containing text, type, description, etc.
261
+ * @returns The created MessageItem instance for the MessagePopover.
262
+ */
263
+ private createMessageItem(message: Message): MessageItem {
264
+ const sDescriptionUrl: string | undefined = message.getDescriptionUrl?.();
148
265
 
149
- showMessages(messages: Message[]): void {
150
- this._showMessages(messages);
266
+ return new MessageItem({
267
+ title: message.getMessage(),
268
+ description: message.getDescription(),
269
+ type: message.getType(),
270
+ longtextUrl: sDescriptionUrl,
271
+ subtitle: message.getAdditionalText?.(),
272
+ groupName: "T_MESSAGE_BUTTON_SAPFE_MESSAGE_GROUP_GENERAL",
273
+ activeTitle: !!message.getControlIds?.().length
274
+ });
275
+ }
276
+
277
+ /**
278
+ * Creates a MessagePopover for a set of MessageItems and attaches
279
+ * an async description handler to load long text when needed.
280
+ * @param messageItems Array of MessageItems to be displayed in the popover.
281
+ * @returns The created MessagePopover instance.
282
+ */
283
+ createMessagePopover(messageItems: MessageItem): MessagePopover {
284
+ const popover = new MessagePopover({
285
+ items: messageItems
286
+ }) as MessagePopover;
287
+ return popover;
288
+ }
289
+
290
+ /**
291
+ * Opens a MessagePopover for the provided messages, anchored to the
292
+ * source control of the UI5 event.
293
+ * @param event The UI5 event from the control that triggers the popover.
294
+ * @param message Array of Message objects to be shown in the popover.
295
+ * @returns The MessagePopover that was opened.
296
+ */
297
+ showMessagePopover(event: UI5Event, message: Message[]): MessagePopover {
298
+ const messageItem = this.createMessageItem(message[0]);
299
+ const popover = this.createMessagePopover(messageItem);
300
+ const sourceControl = event.getSource?.() as Control;
301
+ sourceControl?.addDependent(popover);
302
+ popover.openBy(sourceControl);
303
+ return popover;
151
304
  }
152
305
 
153
306
  /**
154
307
  * Displays the message strip between the title and the header of the ObjectPage.
155
308
  * @param messages The message to be displayed
156
- * @param origin The origin of the message . It may come from a backend message or a custom message
309
+ * @param origin The origin of the message. It may come from a back-end message or a custom message
157
310
  */
158
-
159
311
  _showMessages(messages: Message[], origin: "Backend" | "Custom" = "Custom"): void {
160
312
  try {
313
+ this.removeCustomMessageStrip();
314
+
161
315
  const view = this._view;
162
316
  const internalModel = view.getModel("internal");
163
317
  const messagestripInternalModelContext = internalModel.bindContext(this._getMessageStripBindingContextPath()).getBoundContext();
@@ -165,14 +319,53 @@ class ObjectPageExtensionAPI extends ExtensionAPI {
165
319
  return;
166
320
  }
167
321
  const resourceModel = ResourceModelHelper.getResourceModel(view);
168
- let message: Message | null = null;
322
+ let message: Message | null = messages[0] || null;
169
323
  messagestripInternalModelContext.setProperty(`OP${origin}MessageVisible`, !!message);
170
324
  switch (messages.length) {
171
325
  case 0:
172
326
  break;
173
327
  case 1:
174
328
  message = messages[0];
175
- messagestripInternalModelContext.setProperty(`OP${origin}MessageVisible`, !!message);
329
+ const hasDescriptionUrl = !!message?.getDescriptionUrl();
330
+ const isEditMode = CommonUtils.getIsEditable(view);
331
+ try {
332
+ const objectPage = view.getContent()?.[0] as ObjectPageLayout;
333
+ const headerTitle = objectPage.getHeaderTitle() as ObjectPageDynamicHeaderTitle;
334
+
335
+ // Collect all MessageStrips (excluding Collaboration Strips)
336
+ const allStrips = [
337
+ ...(headerTitle.getSnappedContent?.() || []),
338
+ ...(headerTitle.getExpandedContent?.() || [])
339
+ ].filter(
340
+ (item): item is MessageStrip => item instanceof MessageStrip && !item.getId()?.includes("CollaborationStrip")
341
+ );
342
+
343
+ // Only add links when message is from Backend, in Display mode, and has a description URL
344
+ if (origin === "Backend" && !isEditMode && hasDescriptionUrl) {
345
+ allStrips.forEach((strip) => {
346
+ // Always destroy the existing link before creating a new one
347
+ if (strip.getLink()) {
348
+ strip.destroyLink();
349
+ }
350
+ const link = new Link({
351
+ text: resourceModel.getText("C_MESSAGE_HANDLING_SAPFE_ERROR_MESSAGE_STRIP_LINK_TEXT"),
352
+ press: (event: UI5Event): void => {
353
+ this.showMessagePopover(event, [message!]);
354
+ }
355
+ });
356
+ strip.setLink(link);
357
+ });
358
+ } else {
359
+ // Remove links when not applicable
360
+ allStrips.forEach((strip) => {
361
+ if (strip.getLink()) {
362
+ strip.destroyLink();
363
+ }
364
+ });
365
+ }
366
+ } catch (err) {
367
+ Log.warning("Unable to attach link to ObjectPage MessageStrip", err as Error);
368
+ }
176
369
  break;
177
370
  default:
178
371
  const messageStats: { [key: string]: { id: number; count: number } } = {
@@ -218,13 +411,16 @@ class ObjectPageExtensionAPI extends ExtensionAPI {
218
411
  * @public
219
412
  */
220
413
  hideMessage(): void {
221
- const internalModel = this._view.getModel("internal");
222
- const messagestripInternalModelContext = internalModel.bindContext(this._getMessageStripBindingContextPath()).getBoundContext()!;
414
+ const messagestripInternalModelContext = this._view
415
+ .getModel("internal")
416
+ .bindContext(this._getMessageStripBindingContextPath())
417
+ .getBoundContext()!;
223
418
  messagestripInternalModelContext.setProperty(`OPCustomMessageVisible`, false);
419
+ this.removeCustomMessageStrip();
224
420
  }
225
421
 
226
422
  /**
227
- * This function will take the recommendation data details, transform it and update internal model with that.
423
+ * This function will take the recommendation data details, transform it, and update the internal model.
228
424
  * @param data Recommendation data for the app
229
425
  */
230
426
  setRecommendations(data: InCompletenessInfoType): void {
@@ -262,7 +458,7 @@ class ObjectPageExtensionAPI extends ExtensionAPI {
262
458
  }
263
459
  if (section) {
264
460
  if (reuseComponent && section.getVisibleSubSections().length > 1) {
265
- section.adjustForSingleContent(originalControl, { multipleSubSectionsWithReuseComponent : true });
461
+ section.adjustForSingleContent(originalControl, { multipleSubSectionsWithReuseComponent: true });
266
462
  } else {
267
463
  section.adjustForSingleContent(originalControl);
268
464
  }
@@ -119,6 +119,10 @@
119
119
  >
120
120
  <core:Fragment fragmentName="sap.fe.templates.ObjectPage.view.fragments.Heading" type="XML" />
121
121
  <uxap:content>
122
+ <core:InvisibleText
123
+ id="fe::AiAction::AriaText"
124
+ text="{sap.fe.i18n>C_ACCESSIBILITY_AI_ACTION_ARIA_TEXT}"
125
+ />
122
126
  <template:if test="{entitySet>@com.sap.vocabularies.Common.v1.DraftRoot}">
123
127
  <macro:draftIndicator.DraftIndicator
124
128
  draftIndicatorType="IconOnly"