@sapui5/sap.fe.templates 1.149.0 → 1.150.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.
- package/package.json +1 -1
- package/src/sap/fe/templates/.library +1 -1
- package/src/sap/fe/templates/AnalyticalListPage/manifest.json +1 -1
- package/src/sap/fe/templates/ListReport/ListReportController.controller.js +82 -3
- package/src/sap/fe/templates/ListReport/ListReportController.controller.ts +74 -2
- package/src/sap/fe/templates/ListReport/manifest.json +1 -1
- package/src/sap/fe/templates/ListReport/view/fragments/Table.fragment.xml +1 -0
- package/src/sap/fe/templates/ListReport/view/fragments/VariantManagement.fragment.xml +1 -0
- package/src/sap/fe/templates/ObjectPage/ObjectPage.view.xml +4 -2
- package/src/sap/fe/templates/ObjectPage/ObjectPageController.controller.js +102 -73
- package/src/sap/fe/templates/ObjectPage/ObjectPageController.controller.ts +102 -91
- package/src/sap/fe/templates/ObjectPage/components/CollaborationDraft.js +19 -1
- package/src/sap/fe/templates/ObjectPage/components/CollaborationDraft.tsx +17 -0
- package/src/sap/fe/templates/ObjectPage/controls/SubSectionBlock.js +3 -2
- package/src/sap/fe/templates/ObjectPage/controls/SubSectionBlock.ts +4 -1
- package/src/sap/fe/templates/ObjectPage/manifest.json +1 -1
- package/src/sap/fe/templates/ObjectPage/view/fragments/EditableHeaderFacet.fragment.xml +2 -2
- package/src/sap/fe/templates/ObjectPage/view/fragments/ExpandedHeading.fragment.xml +1 -1
- package/src/sap/fe/templates/ObjectPage/view/fragments/Heading.fragment.xml +7 -3
- package/src/sap/fe/templates/ObjectPage/view/fragments/ObjectPageHeaderForm.fragment.xml +1 -0
- package/src/sap/fe/templates/ObjectPage/view/fragments/Section.fragment.xml +4 -4
- package/src/sap/fe/templates/ObjectPage/view/fragments/Table.fragment.xml +1 -0
- package/src/sap/fe/templates/library.js +1 -1
|
@@ -30,15 +30,17 @@ import type { InternalModelContext } from "sap/fe/core/helpers/ModelHelper";
|
|
|
30
30
|
import ModelHelper from "sap/fe/core/helpers/ModelHelper";
|
|
31
31
|
import FELibrary from "sap/fe/core/library";
|
|
32
32
|
import { type WrappedCard } from "sap/fe/core/services/CollaborationManagerServiceFactory";
|
|
33
|
+
import type { TelemetryVariantEvent } from "sap/fe/core/services/TelemetryServiceFactory";
|
|
33
34
|
import type ChartBlock from "sap/fe/macros/Chart";
|
|
34
35
|
import CommonHelper from "sap/fe/macros/CommonHelper";
|
|
36
|
+
import type FormBlock from "sap/fe/macros/Form";
|
|
35
37
|
import type MacroAPI from "sap/fe/macros/MacroAPI";
|
|
36
38
|
import type MultiValueFieldBlock from "sap/fe/macros/MultiValueField";
|
|
37
39
|
import type TableAPI from "sap/fe/macros/Table";
|
|
40
|
+
import VariantManagementBB from "sap/fe/macros/VariantManagement";
|
|
38
41
|
import type EasyFillButton from "sap/fe/macros/ai/EasyFillButton";
|
|
39
42
|
import type SummarizationButton from "sap/fe/macros/ai/SummarizationButton";
|
|
40
43
|
import type SubSection from "sap/fe/macros/controls/section/SubSection";
|
|
41
|
-
import type FormAPI from "sap/fe/macros/form/FormAPI";
|
|
42
44
|
import type MessageButton from "sap/fe/macros/messages/MessageButton";
|
|
43
45
|
import type ShareAPI from "sap/fe/macros/share/ShareAPI";
|
|
44
46
|
import type TableCreationOptions from "sap/fe/macros/table/TableCreationOptions";
|
|
@@ -100,7 +102,7 @@ import MessageHandlerOverride from "./overrides/MessageHandler";
|
|
|
100
102
|
import PaginatorOverride from "./overrides/Paginator";
|
|
101
103
|
import ShareOverrides from "./overrides/Share";
|
|
102
104
|
import ViewStateOverrides from "./overrides/ViewState";
|
|
103
|
-
|
|
105
|
+
const { VariantType } = VariantManagementBB;
|
|
104
106
|
export type BindingParams = {
|
|
105
107
|
redirectedToNonDraft?: string | undefined;
|
|
106
108
|
reason?: NavigationReason;
|
|
@@ -184,6 +186,8 @@ class ObjectPageController extends PageController {
|
|
|
184
186
|
|
|
185
187
|
private mergePatchDraft = false;
|
|
186
188
|
|
|
189
|
+
private fclCloseHandlerSet = false;
|
|
190
|
+
|
|
187
191
|
private autoQueueLock: ReturnType<ODataModel["lock"]> | undefined;
|
|
188
192
|
|
|
189
193
|
private messageBinding?: Binding;
|
|
@@ -353,7 +357,9 @@ class ObjectPageController extends PageController {
|
|
|
353
357
|
*/
|
|
354
358
|
private checkSectionsForNonResponsiveTable(subSections: ObjectPageSubSection[]): void {
|
|
355
359
|
const changeClassForTables = (event: Event, visibleSubSection: ObjectPageSubSection): void => {
|
|
356
|
-
const blocks = [...visibleSubSection.getBlocks(), ...visibleSubSection.getMoreBlocks()]
|
|
360
|
+
const blocks = [...visibleSubSection.getBlocks(), ...visibleSubSection.getMoreBlocks()].filter((block) =>
|
|
361
|
+
block.isA("sap.fe.templates.ObjectPage.controls.SubSectionBlock")
|
|
362
|
+
);
|
|
357
363
|
if (blocks.length === 1) {
|
|
358
364
|
const table = this.searchTableInBlock(blocks[0] as SubSectionBlock);
|
|
359
365
|
if (!table) {
|
|
@@ -671,7 +677,7 @@ class ObjectPageController extends PageController {
|
|
|
671
677
|
_getFirstEmptyMandatoryFieldFromSubSection(aSubSections: ObjectPageSubSection[]): InputBase | undefined {
|
|
672
678
|
if (aSubSections.length === 0) return undefined;
|
|
673
679
|
for (const subSection of aSubSections) {
|
|
674
|
-
const aBlocks = subSection.getBlocks() as (Form |
|
|
680
|
+
const aBlocks = subSection.getBlocks() as (Form | FormBlock)[];
|
|
675
681
|
|
|
676
682
|
if (aBlocks) {
|
|
677
683
|
for (const blockControl of aBlocks) {
|
|
@@ -682,8 +688,8 @@ class ObjectPageController extends PageController {
|
|
|
682
688
|
aFormContainers = blockControl.getFormContainers();
|
|
683
689
|
} else if (blockControl.getContent && blockControl.getContent()?.isA<Form>("sap.ui.layout.form.Form")) {
|
|
684
690
|
aFormContainers = (blockControl.getContent() as Form).getFormContainers();
|
|
685
|
-
} else if (blockControl?.getContent?.()?.isA<
|
|
686
|
-
aFormContainers = ((blockControl.getContent() as
|
|
691
|
+
} else if (blockControl?.getContent?.()?.isA<FormBlock>("sap.fe.macros.Form")) {
|
|
692
|
+
aFormContainers = ((blockControl.getContent() as FormBlock).getContent() as Form).getFormContainers();
|
|
687
693
|
}
|
|
688
694
|
if (aFormContainers) {
|
|
689
695
|
for (const formContainer of aFormContainers) {
|
|
@@ -835,69 +841,6 @@ class ObjectPageController extends PageController {
|
|
|
835
841
|
oSubSection.getActions().forEach((actions) => actions.setBindingContext(undefined));
|
|
836
842
|
}
|
|
837
843
|
|
|
838
|
-
_onBackNavigationInDraft(oContext: ODataV4Context): void {
|
|
839
|
-
this.messageHandler.removeTransitionMessages();
|
|
840
|
-
|
|
841
|
-
// Function to navigate back, or display the launchpad if we're on the first page of the history
|
|
842
|
-
const navBack = (): void => {
|
|
843
|
-
// Use the new Navigation API if it's available in the browser, to check if we can go back in history.
|
|
844
|
-
const canGoBack = (window as { navigation?: { canGoBack?: boolean } }).navigation?.canGoBack;
|
|
845
|
-
if (canGoBack === true) {
|
|
846
|
-
history.back();
|
|
847
|
-
} else if (canGoBack === false) {
|
|
848
|
-
this._routing.navigateBackFromContext(oContext);
|
|
849
|
-
} else {
|
|
850
|
-
// Fallback in case the browser doesn't support the Navigation API
|
|
851
|
-
const currentURL = document.URL;
|
|
852
|
-
history.back();
|
|
853
|
-
// In case there is no previous page in the history, history.back does nothing.
|
|
854
|
-
// In this case, we need to use navigateBackFromContext, that will display the home page
|
|
855
|
-
setTimeout(() => {
|
|
856
|
-
if (document.URL === currentURL) {
|
|
857
|
-
this._routing.navigateBackFromContext(oContext);
|
|
858
|
-
}
|
|
859
|
-
}, 500);
|
|
860
|
-
}
|
|
861
|
-
};
|
|
862
|
-
|
|
863
|
-
if (this.getAppComponent().getRouterProxy().checkIfBackHasSameContext()) {
|
|
864
|
-
// Back nav will keep the same context --> no need to display the dialog
|
|
865
|
-
history.back();
|
|
866
|
-
} else if (!this.getView().getBindingContext()) {
|
|
867
|
-
// This object page doesn't have a binding context, but still handles the shell back navigation --> pform the nav back and remove the navback handler as it makes no sense any longer
|
|
868
|
-
this.getAppComponent()
|
|
869
|
-
.getShellServices()
|
|
870
|
-
.setBackNavigation(undefined)
|
|
871
|
-
.then(navBack)
|
|
872
|
-
.catch((e) => {
|
|
873
|
-
Log.warning("Error while setting back navigation", e);
|
|
874
|
-
});
|
|
875
|
-
} else {
|
|
876
|
-
const hiddenDraftEnabled = (this.getAppComponent()?.getEnvironmentCapabilities()?.getCapabilities()?.HiddenDraft as HiddenDraft)
|
|
877
|
-
?.enabled;
|
|
878
|
-
draft.processDataLossOrDraftDiscardConfirmation(
|
|
879
|
-
() => {
|
|
880
|
-
if (this.getAppComponent().getRouterProxy().checkIfBackIsOutOfGuard()) {
|
|
881
|
-
this.editFlow.handleSessionOff();
|
|
882
|
-
}
|
|
883
|
-
navBack();
|
|
884
|
-
},
|
|
885
|
-
Function.prototype,
|
|
886
|
-
oContext,
|
|
887
|
-
this,
|
|
888
|
-
true,
|
|
889
|
-
draft.NavigationType.BackNavigation,
|
|
890
|
-
undefined,
|
|
891
|
-
hiddenDraftEnabled ? true : undefined,
|
|
892
|
-
undefined,
|
|
893
|
-
// `navigationType` (BackNavigation above) still drives the non-FCL paths inside
|
|
894
|
-
// processDraftAdminData (e.g. processNoActiveEntityMode / processEditingDraftForExistingEntity).
|
|
895
|
-
// `draftDataLossStrategy` is consumed by the FCL branch (processFclMode) only — both are needed.
|
|
896
|
-
{ withoutChanges: draft.DraftDataLossOptions.Discard }
|
|
897
|
-
);
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
|
|
901
844
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
902
845
|
_onAfterBinding(inputBindingContext: ODataV4Context | undefined, mParameters: BindingParams | undefined): void {
|
|
903
846
|
const view = this.getView();
|
|
@@ -1095,6 +1038,13 @@ class ObjectPageController extends PageController {
|
|
|
1095
1038
|
if (!targetControlId) {
|
|
1096
1039
|
return;
|
|
1097
1040
|
}
|
|
1041
|
+
// Reset stale flag so ViewState.apply() is not blocked when the OP is re-activated
|
|
1042
|
+
// via shell keep-alive (cross-app re-entry) without _onBeforeBinding firing.
|
|
1043
|
+
// Intentional ordering: reset to false here before pollForSectionStability (below) sets it back
|
|
1044
|
+
// to true, so any concurrent ViewState.apply() triggered on re-entry is unblocked immediately.
|
|
1045
|
+
if (xAppStateTargetControlId) {
|
|
1046
|
+
this.skipViewStateSectionRestore = false;
|
|
1047
|
+
}
|
|
1098
1048
|
|
|
1099
1049
|
// Wait for visibility bindings (needed for lazy loading scenarios)
|
|
1100
1050
|
try {
|
|
@@ -1144,22 +1094,22 @@ class ObjectPageController extends PageController {
|
|
|
1144
1094
|
return;
|
|
1145
1095
|
}
|
|
1146
1096
|
|
|
1147
|
-
// Poll to detect UX rules reset
|
|
1148
|
-
// _adjustSelectedSectionByUXRules()
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1097
|
+
// Poll to detect UX rules reset. The guard on isNewBindingContext was removed because
|
|
1098
|
+
// _adjustSelectedSectionByUXRules() can also reset the section on cross-app re-entry to
|
|
1099
|
+
// the same entity (shell keep-alive). Polling is lightweight (max 10 × 50 ms) and safe to
|
|
1100
|
+
// run unconditionally whenever we need to hold a target section.
|
|
1101
|
+
pollForSectionStability({
|
|
1102
|
+
getSelectedSection: () => oObjectPage.getSelectedSection(),
|
|
1103
|
+
targetSectionId,
|
|
1104
|
+
onResetDetected: navigateToTarget
|
|
1105
|
+
})
|
|
1106
|
+
.then(() => {
|
|
1107
|
+
this.skipViewStateSectionRestore = true;
|
|
1108
|
+
return; // Required by linter rule promise/always-return
|
|
1154
1109
|
})
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
})
|
|
1159
|
-
.catch(() => {
|
|
1160
|
-
// Required by linter rule promise/catch-or-return - polling failures are non-critical
|
|
1161
|
-
});
|
|
1162
|
-
}
|
|
1110
|
+
.catch(() => {
|
|
1111
|
+
// Required by linter rule promise/catch-or-return - polling failures are non-critical
|
|
1112
|
+
});
|
|
1163
1113
|
}
|
|
1164
1114
|
|
|
1165
1115
|
/**
|
|
@@ -1177,9 +1127,48 @@ class ObjectPageController extends PageController {
|
|
|
1177
1127
|
}
|
|
1178
1128
|
|
|
1179
1129
|
/**
|
|
1180
|
-
*
|
|
1181
|
-
* @param
|
|
1130
|
+
* Tracks telemetry when a new variant is saved.
|
|
1131
|
+
* @param event The save event.
|
|
1132
|
+
*/
|
|
1133
|
+
private async trackVariantSavedTelemetry(event: UI5Event<Record<string, unknown>>): Promise<void> {
|
|
1134
|
+
if (event.getParameter("overwrite")) {
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
if (!event.getSource().isA("sap.fe.macros.Table")) {
|
|
1138
|
+
return;
|
|
1139
|
+
}
|
|
1140
|
+
if (!(await this.getAppComponent()?.getTelemetryService()?.isEnabled())) {
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
const tableAPI = event.getSource() as unknown as TableAPI;
|
|
1144
|
+
this.fireTelemetry(tableAPI?.getContent?.()?.getVariant?.()?.getVariants?.()?.length ?? 0, VariantType.OBJECT_PAGE_TABLE);
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
/**
|
|
1148
|
+
* Tracks telemetry when variants are managed (e.g. deleted).
|
|
1149
|
+
* @param event The manage event.
|
|
1150
|
+
*/
|
|
1151
|
+
private async trackVariantManagedTelemetry(event: UI5Event<Record<string, unknown>>): Promise<void> {
|
|
1152
|
+
const deletedCount: number = ((event.getParameter("deleted") as string[] | undefined) ?? []).length;
|
|
1153
|
+
if (deletedCount === 0) {
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
if (!(await this.getAppComponent()?.getTelemetryService()?.isEnabled())) {
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1159
|
+
this.fireTelemetry(deletedCount, VariantType.OBJECT_PAGE_TABLE);
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
/**
|
|
1163
|
+
* Stores a FE.Variant telemetry event.
|
|
1164
|
+
* @param countVariants Number of variants affected by the action.
|
|
1165
|
+
* @param variantType The variant type discriminator.
|
|
1182
1166
|
*/
|
|
1167
|
+
private fireTelemetry(countVariants: number, variantType: TelemetryVariantEvent["parameters"]["variantType"]): void {
|
|
1168
|
+
const telemetryEvent: TelemetryVariantEvent = { type: "FE.Variant", parameters: { countVariants, variantType } };
|
|
1169
|
+
this.getAppComponent()?.getTelemetryService()?.storeAction(telemetryEvent);
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1183
1172
|
private navigateWithConfirmation(targetControlId: string): void {
|
|
1184
1173
|
// Set targetSubSection BEFORE navigation for app state saving.
|
|
1185
1174
|
// scrollToSection() doesn't fire the navigate event, so onNavigateChange won't be called.
|
|
@@ -1335,12 +1324,13 @@ class ObjectPageController extends PageController {
|
|
|
1335
1324
|
const oView = this.getView();
|
|
1336
1325
|
const oInternalModelContext = oView.getBindingContext("internal") as InternalModelContext;
|
|
1337
1326
|
const oBindingContext = oView.getBindingContext();
|
|
1338
|
-
//Show popup while navigating back from object page in case of draft
|
|
1327
|
+
// Show popup while navigating back from object page in case of draft (incl. hidden draft).
|
|
1328
|
+
// Sticky's shell back is registered centrally from EditFlow.registerEditLifecycleCallbacks.
|
|
1339
1329
|
if (oBindingContext) {
|
|
1340
|
-
const
|
|
1341
|
-
if (
|
|
1330
|
+
const isDraftSupported = ModelHelper.isDraftSupported(oBindingContext.getModel().getMetaModel(), oBindingContext.getPath());
|
|
1331
|
+
if (isDraftSupported) {
|
|
1342
1332
|
const oAppComponent = CommonUtils.getAppComponent(oView);
|
|
1343
|
-
await oAppComponent.getShellServices().setBackNavigation(() => this.
|
|
1333
|
+
await oAppComponent.getShellServices().setBackNavigation(() => this.editFlow.onBackNavigationInDraft(oBindingContext));
|
|
1344
1334
|
}
|
|
1345
1335
|
}
|
|
1346
1336
|
// do not request recommendations action if we are in Display mode
|
|
@@ -1386,6 +1376,22 @@ class ObjectPageController extends PageController {
|
|
|
1386
1376
|
|
|
1387
1377
|
this.mergePatchDraft = true;
|
|
1388
1378
|
}
|
|
1379
|
+
|
|
1380
|
+
// When the user clicks the FCL Close button while a key property field has focus,
|
|
1381
|
+
// the field's focus-loss triggers side effects that refresh the OData binding and
|
|
1382
|
+
// re-render the ObjectPage header title — removing the Close button from the DOM
|
|
1383
|
+
// before the click event fires, so the press is silently lost.
|
|
1384
|
+
// Listening on mousedown (which fires before focus-loss) ensures closeColumn() is
|
|
1385
|
+
// always called regardless of the subsequent re-render.
|
|
1386
|
+
if (!this.fclCloseHandlerSet && !Device.support.touch) {
|
|
1387
|
+
const fclCloseButton = oView.byId("fe::FCLStandardAction::Close");
|
|
1388
|
+
if (fclCloseButton?.isA<Button>("sap.m.Button")) {
|
|
1389
|
+
fclCloseButton.attachBrowserEvent("mousedown", (): void => {
|
|
1390
|
+
this._routing.closeColumn();
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
this.fclCloseHandlerSet = true;
|
|
1394
|
+
}
|
|
1389
1395
|
}
|
|
1390
1396
|
|
|
1391
1397
|
/**
|
|
@@ -1702,6 +1708,7 @@ class ObjectPageController extends PageController {
|
|
|
1702
1708
|
if (error !== RecommendationDialogDecision.Continue) {
|
|
1703
1709
|
messagePopoverErr = (error as string) ?? "";
|
|
1704
1710
|
}
|
|
1711
|
+
throw error;
|
|
1705
1712
|
}
|
|
1706
1713
|
} finally {
|
|
1707
1714
|
if (BusyLocker.isLocked(model)) {
|
|
@@ -2442,11 +2449,15 @@ class ObjectPageController extends PageController {
|
|
|
2442
2449
|
onVariantSelected: function (this: ObjectPageController): void {
|
|
2443
2450
|
this.getExtensionAPI().updateAppState();
|
|
2444
2451
|
},
|
|
2445
|
-
onVariantSaved: function (this: ObjectPageController): void {
|
|
2452
|
+
onVariantSaved: function (this: ObjectPageController, event: UI5Event<Record<string, unknown>>): void {
|
|
2446
2453
|
//TODO: Should remove this setTimeOut once Variant Management provides an api to fetch the current variant key on save
|
|
2447
2454
|
setTimeout(() => {
|
|
2448
2455
|
this.getExtensionAPI().updateAppState();
|
|
2449
2456
|
}, 2000);
|
|
2457
|
+
this.trackVariantSavedTelemetry(event);
|
|
2458
|
+
},
|
|
2459
|
+
onVariantManaged: function (this: ObjectPageController, event: UI5Event<Record<string, unknown>>): void {
|
|
2460
|
+
this.trackVariantManagedTelemetry(event);
|
|
2450
2461
|
},
|
|
2451
2462
|
navigateToSubSection: function (oController: ObjectPageController, vDetailConfig: string | object): void {
|
|
2452
2463
|
const oDetailConfig = typeof vDetailConfig === "string" ? JSON.parse(vDetailConfig) : vDetailConfig;
|