@sap-ux/preview-middleware 0.13.42 → 0.13.43

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.
@@ -57,6 +57,18 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/model/json/JSONModel", "../api-hand
57
57
  extensionPointName = selectedItem.getText();
58
58
  }
59
59
  this.model.setProperty('/extensionPointName', extensionPointName);
60
+ const hasDefaultContent = this.hasDefaultContentForName(extensionPointName);
61
+ this.model.setProperty('/hasDefaultContent', hasDefaultContent);
62
+ },
63
+ /**
64
+ * Determines whether there is default content associated with the given name.
65
+ *
66
+ * @param {string} name - The name to check for associated default content.
67
+ * @returns {boolean} - True if there is non-empty default content associated with the name, false otherwise.
68
+ */
69
+ hasDefaultContentForName: function _hasDefaultContentForName(name) {
70
+ const defaultContent = this.data?.info.find(v => v.name === name)?.defaultContent;
71
+ return defaultContent ? defaultContent.length > 0 : false;
60
72
  },
61
73
  /**
62
74
  * Builds data that is used in the dialog
@@ -64,20 +76,24 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/model/json/JSONModel", "../api-hand
64
76
  buildDialogData: async function _buildDialogData() {
65
77
  const name = this.data?.name;
66
78
  if (name) {
79
+ const hasDefaultContent = this.hasDefaultContentForName(name);
67
80
  const extensionPointList = [{
68
81
  key: 0,
69
82
  value: name
70
83
  }];
71
- this.updateModel(name, 0, extensionPointList, false);
84
+ this.updateModel(name, 0, extensionPointList, false, hasDefaultContent);
72
85
  } else {
86
+ // In this case we are selecting from the application. There can be many extension points under one control.
73
87
  const extensionPointList = this.data.info.map((v, idx) => {
74
88
  return {
75
89
  key: idx,
76
90
  value: v.name
77
91
  };
78
92
  });
93
+ const firstElName = extensionPointList[0].value;
79
94
  const enabled = extensionPointList.length > 1;
80
- this.updateModel(extensionPointList[0].value, 0, extensionPointList, enabled);
95
+ const hasDefaultContent = this.hasDefaultContentForName(firstElName);
96
+ this.updateModel(firstElName, 0, extensionPointList, enabled, hasDefaultContent);
81
97
  }
82
98
  try {
83
99
  const {
@@ -97,11 +113,12 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/model/json/JSONModel", "../api-hand
97
113
  * @param list All of the extension points that are under a view
98
114
  * @param enabled Enables the select control
99
115
  */
100
- updateModel: function _updateModel(name, key, list, enabled) {
116
+ updateModel: function _updateModel(name, key, list, enabled, hasDefaultContent) {
101
117
  this.model.setProperty('/extensionPointName', name);
102
118
  this.model.setProperty('/extensionPointKey', key);
103
119
  this.model.setProperty('/extensionPointList', list);
104
120
  this.model.setProperty('/extensionListEnabled', enabled);
121
+ this.model.setProperty('/hasDefaultContent', hasDefaultContent);
105
122
  },
106
123
  /**
107
124
  * Creates add xml at extension point changes
@@ -85,6 +85,19 @@ export default class ExtensionPoint extends BaseDialog {
85
85
  }
86
86
 
87
87
  this.model.setProperty('/extensionPointName', extensionPointName);
88
+ const hasDefaultContent = this.hasDefaultContentForName(extensionPointName);
89
+ this.model.setProperty('/hasDefaultContent', hasDefaultContent);
90
+ }
91
+
92
+ /**
93
+ * Determines whether there is default content associated with the given name.
94
+ *
95
+ * @param {string} name - The name to check for associated default content.
96
+ * @returns {boolean} - True if there is non-empty default content associated with the name, false otherwise.
97
+ */
98
+ private hasDefaultContentForName(name: string): boolean {
99
+ const defaultContent = this.data?.info.find((v) => v.name === name)?.defaultContent;
100
+ return defaultContent ? defaultContent.length > 0 : false;
88
101
  }
89
102
 
90
103
  /**
@@ -92,18 +105,26 @@ export default class ExtensionPoint extends BaseDialog {
92
105
  */
93
106
  async buildDialogData(): Promise<void> {
94
107
  const name = this.data?.name;
108
+
95
109
  if (name) {
110
+ const hasDefaultContent = this.hasDefaultContentForName(name);
111
+
96
112
  const extensionPointList = [{ key: 0, value: name }];
97
- this.updateModel(name, 0, extensionPointList, false);
113
+
114
+ this.updateModel(name, 0, extensionPointList, false, hasDefaultContent);
98
115
  } else {
116
+ // In this case we are selecting from the application. There can be many extension points under one control.
99
117
  const extensionPointList = this.data.info.map((v: ExtensionPointInfo, idx: number) => {
100
118
  return {
101
119
  key: idx,
102
120
  value: v.name
103
121
  };
104
122
  });
123
+ const firstElName = extensionPointList[0].value;
105
124
  const enabled = extensionPointList.length > 1;
106
- this.updateModel(extensionPointList[0].value, 0, extensionPointList, enabled);
125
+ const hasDefaultContent = this.hasDefaultContentForName(firstElName);
126
+
127
+ this.updateModel(firstElName, 0, extensionPointList, enabled, hasDefaultContent);
107
128
  }
108
129
 
109
130
  try {
@@ -124,11 +145,18 @@ export default class ExtensionPoint extends BaseDialog {
124
145
  * @param list All of the extension points that are under a view
125
146
  * @param enabled Enables the select control
126
147
  */
127
- private updateModel(name: string, key: number, list: { key: number; value: string }[], enabled: boolean): void {
148
+ private updateModel(
149
+ name: string,
150
+ key: number,
151
+ list: { key: number; value: string }[],
152
+ enabled: boolean,
153
+ hasDefaultContent: boolean
154
+ ): void {
128
155
  this.model.setProperty('/extensionPointName', name);
129
156
  this.model.setProperty('/extensionPointKey', key);
130
157
  this.model.setProperty('/extensionPointList', list);
131
158
  this.model.setProperty('/extensionListEnabled', enabled);
159
+ this.model.setProperty('/hasDefaultContent', hasDefaultContent);
132
160
  }
133
161
 
134
162
  /**
@@ -24,11 +24,11 @@ export interface ExtensionPointInfo {
24
24
  name: string;
25
25
  index?: number;
26
26
  view?: View;
27
- createdControls?: [];
27
+ createdControls: string[];
28
28
  fragmentId?: string;
29
29
  aggregation?: string[];
30
30
  aggregationName?: string;
31
- defaultContent?: string[];
31
+ defaultContent: UI5Element[];
32
32
  targetControl?: UI5Element;
33
33
  }
34
34
 
@@ -81,7 +81,7 @@ export default class ExtensionPointService {
81
81
 
82
82
  const plugin = new AddXMLAtExtensionPoint({
83
83
  commandFactory,
84
- fragmentHandler: async (overlay: UI5Element, info: ExtensionPointData[]) =>
84
+ fragmentHandler: async (overlay: UI5Element, info: ExtensionPointInfo[]) =>
85
85
  await this.fragmentHandler(overlay, info)
86
86
  });
87
87
 
@@ -97,7 +97,7 @@ export default class ExtensionPointService {
97
97
  * @param info Extension point data from the plugin
98
98
  * @returns Deferred extension point data that is provided to the plugin
99
99
  */
100
- public async fragmentHandler(overlay: UI5Element, info: ExtensionPointData[]): Promise<DeferredExtPointData> {
100
+ public async fragmentHandler(overlay: UI5Element, info: ExtensionPointInfo[]): Promise<DeferredExtPointData> {
101
101
  let deferred = createDeferred<DeferredExtPointData>();
102
102
  const name = this.selectedExtensionPointName;
103
103
 
@@ -105,7 +105,7 @@ export default class ExtensionPointService {
105
105
  name,
106
106
  info,
107
107
  deferred
108
- } as ExtensionPointData);
108
+ });
109
109
 
110
110
  this.selectedExtensionPointName = '';
111
111
  return deferred.promise;
@@ -13,6 +13,14 @@
13
13
  labelSpanS="4"
14
14
  singleContainerFullSize="false">
15
15
  <f:content>
16
+ <MessageStrip
17
+ text="Have in mind that the selected extension point has a default content and by adding fragment it will no longer be visible."
18
+ id="extensionPointDefaultContentMessage"
19
+ class="sapUiTinyMarginBottom"
20
+ showIcon="true"
21
+ type="Information"
22
+ visible="{/hasDefaultContent}">
23
+ </MessageStrip>
16
24
  <Label text="Extension Point" />
17
25
  <Select
18
26
  enabled="{/extensionListEnabled}"
@@ -15,6 +15,7 @@ import Log from 'sap/base/Log';
15
15
  export async function initOutline(rta: RuntimeAuthoring, sendAction: (action: ExternalAction) => void): Promise<void> {
16
16
  const outline = await rta.getService<OutlineService>('outline');
17
17
  const scenario = rta.getFlexSettings().scenario;
18
+
18
19
  async function syncOutline() {
19
20
  try {
20
21
  const viewNodes = await outline.get();
@@ -7,20 +7,27 @@ sap.ui.define(["./utils"], function (___utils) {
7
7
  /**
8
8
  * Retrieves additional data for a given control ID.
9
9
  *
10
- * @param id The control ID for which to retrieve additional data
11
- * @returns An object containing additional data, including an optional 'text' property
10
+ * @param id The unique identifier of the control.
11
+ * @returns An object containing the text and the technical name of the control.
12
12
  */
13
13
  function getAdditionalData(id) {
14
14
  const control = sap.ui.getCore().byId(id);
15
- if (control?.getMetadata().getProperty('text')) {
15
+ if (!control) {
16
+ return {};
17
+ }
18
+ const metadata = control.getMetadata();
19
+ let details = {};
20
+ const technicalName = metadata.getElementName();
21
+ if (technicalName) {
22
+ details.technicalName = technicalName;
23
+ }
24
+ if (metadata.getProperty('text')) {
16
25
  const text = control.getProperty('text');
17
26
  if (typeof text === 'string' && text.trim() !== '') {
18
- return {
19
- text
20
- };
27
+ details.text = text;
21
28
  }
22
29
  }
23
- return {};
30
+ return details;
24
31
  }
25
32
 
26
33
  /**
@@ -33,12 +40,36 @@ sap.ui.define(["./utils"], function (___utils) {
33
40
  return (current.elements ?? []).flatMap(element => element.type === 'aggregation' ? element.elements ?? [] : []);
34
41
  }
35
42
 
43
+ /**
44
+ * Adds a new child node to the extension point's children array based on the given control ID.
45
+ *
46
+ * @param {string} id - The unique identifier of the control to be added as a child node.
47
+ * @param {OutlineNode[]} children - The array of children nodes to which the new node will be added.
48
+ */
49
+ function addChildToExtensionPoint(id, children) {
50
+ const {
51
+ text,
52
+ technicalName
53
+ } = getAdditionalData(id);
54
+ const editable = isEditable(id);
55
+ children.push({
56
+ controlId: id,
57
+ controlType: technicalName ?? 'sap.ui.extensionpoint.child',
58
+ name: text ?? id,
59
+ visible: true,
60
+ editable,
61
+ children: [],
62
+ hasDefaultContent: false
63
+ });
64
+ }
65
+
36
66
  /**
37
67
  * Transform node.
38
68
  *
39
69
  * @param input outline view node
40
70
  * @param scenario type of project
41
- * @returns Promise<OutlineNode[]>
71
+ * @param extPointIDs ids that need are filled when extension point has default content or created controls inside
72
+ * @returns {Promise<OutlineNode[]>} transformed outline tree nodes
42
73
  */
43
74
  async function transformNodes(input, scenario) {
44
75
  const stack = [...input];
@@ -46,41 +77,77 @@ sap.ui.define(["./utils"], function (___utils) {
46
77
  while (stack.length) {
47
78
  const current = stack.shift();
48
79
  const editable = isEditable(current?.id);
80
+ const isAdp = scenario === 'ADAPTATION_PROJECT';
81
+ const isExtPoint = current?.type === 'extensionPoint';
49
82
  if (current?.type === 'element') {
50
83
  const children = getChildren(current);
51
84
  const {
52
85
  text
53
86
  } = getAdditionalData(current.id);
54
87
  const technicalName = current.technicalName.split('.').slice(-1)[0];
88
+ const transformedChildren = isAdp ? await handleDuplicateNodes(children, scenario) : await transformNodes(children, scenario);
55
89
  const node = {
56
90
  controlId: current.id,
57
91
  controlType: current.technicalName,
58
92
  name: text ?? technicalName,
59
93
  editable,
60
94
  visible: current.visible ?? true,
61
- children: await transformNodes(children, scenario)
95
+ children: transformedChildren
62
96
  };
63
97
  items.push(node);
64
98
  }
65
- if (scenario === 'ADAPTATION_PROJECT' && current?.type === 'extensionPoint') {
99
+ if (isAdp && isExtPoint) {
100
+ const {
101
+ defaultContent,
102
+ createdControls
103
+ } = current.extensionPointInfo;
104
+ let children = [];
105
+ // We can combine both because there can only be either defaultContent or createdControls for one extension point node.
106
+ [...defaultContent, ...createdControls].forEach(id => {
107
+ addChildToExtensionPoint(id, children);
108
+ });
66
109
  const node = {
67
110
  controlId: current.id,
68
111
  controlType: current.technicalName,
69
- name: current.name,
112
+ name: current.name ?? '',
70
113
  editable,
71
114
  visible: current.visible ?? true,
72
- children: [],
73
- icon: current.icon
115
+ children,
116
+ hasDefaultContent: defaultContent.length > 0
74
117
  };
75
118
  items.push(node);
76
119
  }
77
120
  }
78
121
  return items;
79
122
  }
123
+
124
+ /**
125
+ * Handles duplicate nodes that are retrieved from extension point default content and created controls,
126
+ * if they exist under an extension point these controls are removed from the children array
127
+ *
128
+ * @param children outline view node children
129
+ * @param scenario type of project
130
+ * @returns transformed outline tree nodes
131
+ */
132
+ async function handleDuplicateNodes(children, scenario) {
133
+ const extPointIDs = new Set();
134
+ children.forEach(child => {
135
+ if (child.type === 'extensionPoint') {
136
+ const {
137
+ defaultContent,
138
+ createdControls
139
+ } = child.extensionPointInfo;
140
+ [...defaultContent, ...createdControls].forEach(id => extPointIDs.add(id));
141
+ }
142
+ });
143
+ const uniqueChildren = children.filter(child => !extPointIDs.has(child.id));
144
+ return transformNodes(uniqueChildren, scenario);
145
+ }
80
146
  var __exports = {
81
147
  __esModule: true
82
148
  };
83
149
  __exports.transformNodes = transformNodes;
150
+ __exports.handleDuplicateNodes = handleDuplicateNodes;
84
151
  return __exports;
85
152
  });
86
153
  //# sourceMappingURL=nodes.js.map
@@ -5,23 +5,39 @@ import type { Scenario } from 'sap/ui/fl/Scenario';
5
5
 
6
6
  import { isEditable } from './utils';
7
7
 
8
+ interface AdditionalData {
9
+ text?: string;
10
+ technicalName?: string;
11
+ }
12
+
8
13
  /**
9
14
  * Retrieves additional data for a given control ID.
10
15
  *
11
- * @param id The control ID for which to retrieve additional data
12
- * @returns An object containing additional data, including an optional 'text' property
16
+ * @param id The unique identifier of the control.
17
+ * @returns An object containing the text and the technical name of the control.
13
18
  */
14
- function getAdditionalData(id: string): { text?: string } {
19
+ function getAdditionalData(id: string): AdditionalData {
15
20
  const control = sap.ui.getCore().byId(id);
16
- if (control?.getMetadata().getProperty('text')) {
21
+ if (!control) {
22
+ return {};
23
+ }
24
+
25
+ const metadata = control.getMetadata();
26
+ let details: AdditionalData = {};
27
+
28
+ const technicalName = metadata.getElementName();
29
+ if (technicalName) {
30
+ details.technicalName = technicalName;
31
+ }
32
+
33
+ if (metadata.getProperty('text')) {
17
34
  const text = control.getProperty('text');
18
35
  if (typeof text === 'string' && text.trim() !== '') {
19
- return {
20
- text
21
- };
36
+ details.text = text;
22
37
  }
23
38
  }
24
- return {};
39
+
40
+ return details;
25
41
  }
26
42
 
27
43
  /**
@@ -36,12 +52,34 @@ function getChildren(current: OutlineViewNode): OutlineViewNode[] {
36
52
  );
37
53
  }
38
54
 
55
+ /**
56
+ * Adds a new child node to the extension point's children array based on the given control ID.
57
+ *
58
+ * @param {string} id - The unique identifier of the control to be added as a child node.
59
+ * @param {OutlineNode[]} children - The array of children nodes to which the new node will be added.
60
+ */
61
+ function addChildToExtensionPoint(id: string, children: OutlineNode[]) {
62
+ const { text, technicalName } = getAdditionalData(id);
63
+ const editable = isEditable(id);
64
+
65
+ children.push({
66
+ controlId: id,
67
+ controlType: technicalName ?? 'sap.ui.extensionpoint.child',
68
+ name: text ?? id,
69
+ visible: true,
70
+ editable,
71
+ children: [],
72
+ hasDefaultContent: false
73
+ });
74
+ }
75
+
39
76
  /**
40
77
  * Transform node.
41
78
  *
42
79
  * @param input outline view node
43
80
  * @param scenario type of project
44
- * @returns Promise<OutlineNode[]>
81
+ * @param extPointIDs ids that need are filled when extension point has default content or created controls inside
82
+ * @returns {Promise<OutlineNode[]>} transformed outline tree nodes
45
83
  */
46
84
  export async function transformNodes(input: OutlineViewNode[], scenario: Scenario): Promise<OutlineNode[]> {
47
85
  const stack = [...input];
@@ -49,32 +87,47 @@ export async function transformNodes(input: OutlineViewNode[], scenario: Scenari
49
87
  while (stack.length) {
50
88
  const current = stack.shift();
51
89
  const editable = isEditable(current?.id);
90
+ const isAdp = scenario === 'ADAPTATION_PROJECT';
91
+ const isExtPoint = current?.type === 'extensionPoint';
92
+
52
93
  if (current?.type === 'element') {
53
94
  const children = getChildren(current);
54
95
  const { text } = getAdditionalData(current.id);
55
96
  const technicalName = current.technicalName.split('.').slice(-1)[0];
56
97
 
98
+ const transformedChildren = isAdp
99
+ ? await handleDuplicateNodes(children, scenario)
100
+ : await transformNodes(children, scenario);
101
+
57
102
  const node: OutlineNode = {
58
103
  controlId: current.id,
59
104
  controlType: current.technicalName,
60
105
  name: text ?? technicalName,
61
106
  editable,
62
107
  visible: current.visible ?? true,
63
- children: await transformNodes(children, scenario)
108
+ children: transformedChildren
64
109
  };
65
110
 
66
111
  items.push(node);
67
112
  }
68
113
 
69
- if (scenario === 'ADAPTATION_PROJECT' && current?.type === 'extensionPoint') {
114
+ if (isAdp && isExtPoint) {
115
+ const { defaultContent, createdControls } = current.extensionPointInfo;
116
+
117
+ let children: OutlineNode[] = [];
118
+ // We can combine both because there can only be either defaultContent or createdControls for one extension point node.
119
+ [...defaultContent, ...createdControls].forEach((id: string) => {
120
+ addChildToExtensionPoint(id, children);
121
+ });
122
+
70
123
  const node: OutlineNode = {
71
124
  controlId: current.id,
72
125
  controlType: current.technicalName,
73
- name: current.name!,
126
+ name: current.name ?? '',
74
127
  editable,
75
128
  visible: current.visible ?? true,
76
- children: [],
77
- icon: current.icon
129
+ children,
130
+ hasDefaultContent: defaultContent.length > 0
78
131
  };
79
132
 
80
133
  items.push(node);
@@ -82,3 +135,26 @@ export async function transformNodes(input: OutlineViewNode[], scenario: Scenari
82
135
  }
83
136
  return items;
84
137
  }
138
+
139
+ /**
140
+ * Handles duplicate nodes that are retrieved from extension point default content and created controls,
141
+ * if they exist under an extension point these controls are removed from the children array
142
+ *
143
+ * @param children outline view node children
144
+ * @param scenario type of project
145
+ * @returns transformed outline tree nodes
146
+ */
147
+ export async function handleDuplicateNodes(children: OutlineViewNode[], scenario: Scenario): Promise<OutlineNode[]> {
148
+ const extPointIDs = new Set<string>();
149
+
150
+ children.forEach((child: OutlineViewNode) => {
151
+ if (child.type === 'extensionPoint') {
152
+ const { defaultContent, createdControls } = child.extensionPointInfo;
153
+ [...defaultContent, ...createdControls].forEach((id) => extPointIDs.add(id));
154
+ }
155
+ });
156
+
157
+ const uniqueChildren = children.filter((child) => !extPointIDs.has(child.id));
158
+
159
+ return transformNodes(uniqueChildren, scenario);
160
+ }
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "bugs": {
10
10
  "url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Apreview-middleware"
11
11
  },
12
- "version": "0.13.42",
12
+ "version": "0.13.43",
13
13
  "license": "Apache-2.0",
14
14
  "author": "@SAP/ux-tools-team",
15
15
  "main": "dist/index.js",
@@ -28,7 +28,7 @@
28
28
  "@sap-ux/logger": "0.5.1",
29
29
  "@sap-ux/btp-utils": "0.14.4",
30
30
  "@sap-ux/adp-tooling": "0.10.7",
31
- "@sap-ux/control-property-editor-sources": "npm:@sap-ux/control-property-editor@0.4.20",
31
+ "@sap-ux/control-property-editor-sources": "npm:@sap-ux/control-property-editor@0.4.21",
32
32
  "@sap-ux/project-access": "1.20.0"
33
33
  },
34
34
  "devDependencies": {
@@ -45,7 +45,7 @@
45
45
  "supertest": "6.3.3",
46
46
  "@sap-ux-private/playwright": "0.0.3",
47
47
  "dotenv": "16.3.1",
48
- "@private/preview-middleware-client": "npm:@sap-ux-private/preview-middleware-client@0.9.12",
48
+ "@private/preview-middleware-client": "npm:@sap-ux-private/preview-middleware-client@0.9.13",
49
49
  "@sap-ux/axios-extension": "1.12.4",
50
50
  "@sap-ux/store": "0.5.0",
51
51
  "@sap-ux/ui5-info": "0.5.0",