@sap/ux-ui5-tooling 1.24.0 → 1.25.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.
@@ -200,14 +200,217 @@ interface NewControlData {
200
200
  newValue: unknown;
201
201
  }
202
202
 
203
+ type PropertyDocumentation = {
204
+ defaultValue: string;
205
+ description: string;
206
+ propertyName: string;
207
+ type?: string;
208
+ propertyType?: string;
209
+ };
210
+
211
+ interface ProcessedProperty {
212
+ analyzedType: AnalyzedType;
213
+ isEnabled: boolean;
214
+ value: unknown;
215
+ propertyType: PropertyType;
216
+ docu?: PropertyDocumentation;
217
+ }
218
+
219
+ /**
220
+ * Build the documentation object for a configuration (manifest) property.
221
+ *
222
+ * @param property - manifest property
223
+ * @returns PropertyDocumentation
224
+ */
225
+ function buildConfigPropertyDocumentation(property: MergedSetting): PropertyDocumentation {
226
+ const defValue = ['undefined', 'null'].includes(String(property.defaultValue))
227
+ ? '-'
228
+ : String(property.defaultValue);
229
+ return {
230
+ description: property.description,
231
+ propertyName: property.id,
232
+ type: property.type,
233
+ defaultValue: defValue || '-'
234
+ };
235
+ }
236
+
237
+ /**
238
+ * Process a manifest configuration property.
239
+ *
240
+ * @param property - manifest property
241
+ * @param control - ui5 control
242
+ * @returns ProcessedProperty or undefined if the property should be skipped
243
+ */
244
+ function processConfigProperty(property: MergedSetting, control: ManagedObject): ProcessedProperty | undefined {
245
+ const analyzedType = analyzeManifestProperty(property);
246
+ if (
247
+ !analyzedType ||
248
+ (property?.restrictedTo?.length &&
249
+ !property?.restrictedTo?.includes(getV4PageType(control) as TemplateType))
250
+ ) {
251
+ return undefined;
252
+ }
253
+ return {
254
+ analyzedType,
255
+ isEnabled: true,
256
+ value: property.value,
257
+ propertyType: PropertyType.Configuration,
258
+ docu: buildConfigPropertyDocumentation(property)
259
+ };
260
+ }
261
+
262
+ /**
263
+ * Process a UI5 control property.
264
+ *
265
+ * @param property - control metadata property
266
+ * @param control - ui5 control
267
+ * @param hasStableId - whether the control has a stable ID
268
+ * @param controlProperties - overlay design-time property config
269
+ * @param controlOverlay - element overlay
270
+ * @returns ProcessedProperty or undefined if the property should be skipped
271
+ */
272
+ function processControlProperty(
273
+ property: ManagedObjectMetadataProperties,
274
+ control: ManagedObject,
275
+ hasStableId: boolean,
276
+ controlProperties: Record<string, { ignore: boolean }> | undefined,
277
+ controlOverlay: ElementOverlay | undefined
278
+ ): ProcessedProperty | undefined {
279
+ const analyzedType = analyzePropertyType(property);
280
+ if (!analyzedType) {
281
+ return undefined;
282
+ }
283
+ const ignore = controlProperties?.[property.name]?.ignore ?? false;
284
+
285
+ //updating i18n text for the control if bindingInfo has bindingString
286
+ const controlNewData: NewControlData = {
287
+ id: control.getId(),
288
+ name: property.name,
289
+ newValue: control.getProperty(property.name)
290
+ };
291
+ const bindingInfo: { bindingString?: string } = control.getBindingInfo(controlNewData.name) as {
292
+ bindingString?: string;
293
+ };
294
+ if (bindingInfo?.bindingString !== undefined) {
295
+ controlNewData.newValue = bindingInfo.bindingString;
296
+ }
297
+
298
+ // A property is enabled if:
299
+ // 1. The property supports changes
300
+ // 2. The control has stable ID
301
+ // 3. It is not configured to be ignored in design time
302
+ // 4. And control overlay is selectable
303
+ return {
304
+ analyzedType,
305
+ isEnabled: isControlEnabled(analyzedType, hasStableId, ignore, controlOverlay),
306
+ value: normalizeObjectPropertyValue(controlNewData.newValue),
307
+ propertyType: PropertyType.ControlProperty
308
+ };
309
+ }
310
+
311
+ /**
312
+ * Build a ControlProperty entry from an analyzed property.
313
+ *
314
+ * @param property - the raw property (manifest or control metadata)
315
+ * @param processed - the processed property data
316
+ * @param selectedControlName - name of the UI5 control class
317
+ * @returns ControlProperty or undefined if the type is not supported
318
+ */
319
+ function buildPropertyEntry(
320
+ property: ManagedObjectMetadataProperties | MergedSetting,
321
+ processed: ProcessedProperty,
322
+ selectedControlName: string
323
+ ): ControlProperty | undefined {
324
+ const { analyzedType, isEnabled, value, propertyType, docu } = processed;
325
+ const isIcon =
326
+ testIconPattern(property.name) &&
327
+ selectedControlName !== 'sap.m.Image' &&
328
+ analyzedType.ui5Type === 'sap.ui.core.URI';
329
+ const ui5Type = analyzedType.ui5Type || undefined;
330
+ const readableName = convertCamelCaseToPascalCase(property.name);
331
+ const docuSpread = docu ? { documentation: docu } : {};
332
+
333
+ switch (analyzedType.primitiveType) {
334
+ case 'enum': {
335
+ const values = analyzedType.enumValues ?? {};
336
+ const options: { key: string; text: string }[] = Object.keys(values).map((key) => ({
337
+ key,
338
+ text: values[key]
339
+ }));
340
+ return {
341
+ type: STRING_VALUE_TYPE,
342
+ editor: DROPDOWN_EDITOR_TYPE,
343
+ propertyType,
344
+ name: property.name,
345
+ readableName,
346
+ value: value as string,
347
+ isEnabled,
348
+ ui5Type,
349
+ options,
350
+ ...docuSpread
351
+ };
352
+ }
353
+ case 'string':
354
+ return {
355
+ type: STRING_VALUE_TYPE,
356
+ editor: INPUT_EDITOR_TYPE,
357
+ propertyType,
358
+ name: property.name,
359
+ readableName,
360
+ value: value as string,
361
+ isEnabled,
362
+ isIcon,
363
+ ui5Type,
364
+ ...docuSpread
365
+ };
366
+ case 'int':
367
+ return {
368
+ type: INTEGER_VALUE_TYPE,
369
+ editor: INPUT_EDITOR_TYPE,
370
+ propertyType,
371
+ name: property.name,
372
+ readableName,
373
+ value: value as number,
374
+ isEnabled,
375
+ ui5Type,
376
+ ...docuSpread
377
+ };
378
+ case 'float':
379
+ return {
380
+ type: FLOAT_VALUE_TYPE,
381
+ editor: INPUT_EDITOR_TYPE,
382
+ propertyType,
383
+ name: property.name,
384
+ readableName,
385
+ value: value as number,
386
+ isEnabled,
387
+ ui5Type,
388
+ ...docuSpread
389
+ };
390
+ case 'boolean':
391
+ return {
392
+ type: BOOLEAN_VALUE_TYPE,
393
+ editor: CHECKBOX_EDITOR_TYPE,
394
+ propertyType,
395
+ name: property.name,
396
+ readableName,
397
+ value: value as boolean,
398
+ isEnabled,
399
+ ui5Type,
400
+ ...docuSpread
401
+ };
402
+ default:
403
+ return undefined;
404
+ }
405
+ }
406
+
203
407
  /**
204
408
  * Build control data.
205
409
  *
206
410
  * @param control - ui5 control
207
411
  * @param changeService - Changeservice for change stack event handling.
208
412
  * @param controlOverlay - element overlay
209
- * @param includeDocumentation - include documentation flag
210
- * @returns Promise<Control>
413
+ * @returns Control
211
414
  */
212
415
  export function buildControlData(
213
416
  control: ManagedObject,
@@ -218,182 +421,37 @@ export function buildControlData(
218
421
  const selectedControlName = controlMetadata.getName();
219
422
  const hasStableId = Utils.checkControlId(control);
220
423
  const overlayData = controlOverlay?.getDesignTimeMetadata().getData();
221
-
222
424
  const controlProperties = controlOverlay ? overlayData?.properties : undefined;
425
+
223
426
  const manifestProperties = getManifestProperties(control, changeService, controlOverlay);
224
- // Add the control's properties/manifest properties
225
427
  const allProperties = {
226
428
  ...(controlMetadata.getAllProperties() as unknown as {
227
429
  [name: string]: ManagedObjectMetadataProperties;
228
430
  }),
229
431
  ...manifestProperties
230
432
  };
231
- let propertyType;
232
- const propertyNames = Object.keys(allProperties);
433
+
233
434
  const properties: ControlProperty[] = [];
234
- for (const propertyName of propertyNames) {
435
+ for (const propertyName of Object.keys(allProperties)) {
235
436
  const property = allProperties[propertyName];
236
- let analyzedType;
237
- let isEnabled: boolean;
238
- let value: unknown;
239
- if (property && 'configuration' in property) {
240
- propertyType = PropertyType.Configuration;
241
- analyzedType = analyzeManifestProperty(property);
242
- if (
243
- !analyzedType ||
244
- (property?.restrictedTo?.length &&
245
- !property?.restrictedTo?.includes(getV4PageType(control) as TemplateType))
246
- ) {
247
- continue;
248
- }
249
- isEnabled = true;
250
- value = property.value;
251
- } else {
252
- propertyType = PropertyType.ControlProperty;
253
- // the default behavior is that the property is enabled
254
- // meaning it's not ignored during design time
255
- analyzedType = analyzePropertyType(property);
256
- if (!analyzedType) {
257
- continue;
258
- }
259
- let ignore = false;
260
- if (controlProperties?.[property.name]) {
261
- // check whether the property should be ignored in design time or not
262
- // if it's 'undefined' then it's not considered when building isEnabled because it's 'true'
263
- ignore = controlProperties[property.name].ignore;
264
- }
265
-
266
- //updating i18n text for the control if bindingInfo has bindingString
267
- const controlNewData: NewControlData = {
268
- id: control.getId(),
269
- name: property.name,
270
- newValue: control.getProperty(property.name)
271
- };
272
- const bindingInfo: { bindingString?: string } = control.getBindingInfo(controlNewData.name) as {
273
- bindingString?: string;
274
- };
275
- if (bindingInfo?.bindingString !== undefined) {
276
- controlNewData.newValue = bindingInfo.bindingString;
277
- }
278
-
279
- // A property is enabled if:
280
- // 1. The property supports changes
281
- // 2. The control has stable ID
282
- // 3. It is not configured to be ignored in design time
283
- // 4. And control overlay is selectable
284
- isEnabled = isControlEnabled(analyzedType, hasStableId, ignore, controlOverlay);
437
+ const processed =
438
+ property && 'configuration' in property
439
+ ? processConfigProperty(property, control)
440
+ : processControlProperty(property, control, hasStableId, controlProperties, controlOverlay);
285
441
 
286
- value = normalizeObjectPropertyValue(controlNewData.newValue);
442
+ if (!processed) {
443
+ continue;
287
444
  }
288
- const isIcon =
289
- testIconPattern(property.name) &&
290
- selectedControlName !== 'sap.m.Image' &&
291
- analyzedType.ui5Type === 'sap.ui.core.URI';
292
- const ui5Type = analyzedType.ui5Type || undefined;
293
- const readableName = convertCamelCaseToPascalCase(property.name);
294
- let docu:
295
- | {
296
- defaultValue: string;
297
- description: string;
298
- propertyName: string;
299
- type?: string;
300
- propertyType?: string;
301
- }
302
- | undefined;
303
- if ('configuration' in property) {
304
- const defValue = ['undefined', 'null'].includes(String(property.defaultValue))
305
- ? '-'
306
- : String(property.defaultValue);
307
- docu = {
308
- description: property.description,
309
- propertyName: property.id,
310
- type: property.type,
311
- defaultValue: defValue || '-'
312
- };
313
- }
314
- switch (analyzedType.primitiveType) {
315
- case 'enum': {
316
- const values = analyzedType.enumValues ?? {};
317
- const options: { key: string; text: string }[] = Object.keys(values).map((key) => ({
318
- key,
319
- text: values[key]
320
- }));
321
- properties.push({
322
- type: STRING_VALUE_TYPE,
323
- editor: DROPDOWN_EDITOR_TYPE,
324
- propertyType,
325
- name: property.name,
326
- readableName,
327
- value: value as string,
328
- isEnabled,
329
- ui5Type,
330
- options,
331
- ...(docu && { documentation: docu })
332
- });
333
- break;
334
- }
335
- case 'string': {
336
- properties.push({
337
- type: STRING_VALUE_TYPE,
338
- editor: INPUT_EDITOR_TYPE,
339
- propertyType,
340
- name: property.name,
341
- readableName,
342
- value: value as string,
343
- isEnabled,
344
- isIcon,
345
- ui5Type,
346
- ...(docu && { documentation: docu })
347
- });
348
- break;
349
- }
350
- case 'int': {
351
- properties.push({
352
- type: INTEGER_VALUE_TYPE,
353
- editor: INPUT_EDITOR_TYPE,
354
- propertyType,
355
- name: property.name,
356
- readableName,
357
- value: value as number,
358
- isEnabled,
359
- ui5Type,
360
- ...(docu && { documentation: docu })
361
- });
362
- break;
363
- }
364
- case 'float': {
365
- properties.push({
366
- type: FLOAT_VALUE_TYPE,
367
- editor: INPUT_EDITOR_TYPE,
368
- propertyType,
369
- name: property.name,
370
- readableName,
371
- value: value as number,
372
- isEnabled,
373
- ui5Type,
374
- ...(docu && { documentation: docu })
375
- });
376
- break;
377
- }
378
- case 'boolean': {
379
- properties.push({
380
- type: BOOLEAN_VALUE_TYPE,
381
- editor: CHECKBOX_EDITOR_TYPE,
382
- propertyType,
383
- name: property.name,
384
- readableName,
385
- value: value as boolean,
386
- isEnabled,
387
- ui5Type,
388
- ...(docu && { documentation: docu })
389
- });
390
- break;
391
- }
445
+
446
+ const entry = buildPropertyEntry(property, processed, selectedControlName);
447
+ if (entry) {
448
+ properties.push(entry);
392
449
  }
393
450
  }
451
+
394
452
  return {
395
- id: control.getId(), //the id of the underlying control/aggregation
396
- type: selectedControlName, //the name of the ui5 class of the control/aggregation
453
+ id: control.getId(),
454
+ type: selectedControlName,
397
455
  properties: [...properties].sort((a, b) => (a.name > b.name ? 1 : -1)),
398
456
  name: selectedControlName
399
457
  };
@@ -150,9 +150,9 @@ sap.ui.define(["sap/base/Log", "open/ux/preview/client/thirdparty/@sap-ux-privat
150
150
  async function registerComponentDependencyPaths(appUrls, urlParams) {
151
151
  const libs = await getManifestLibs(appUrls);
152
152
  if (libs && libs.length > 0) {
153
- let url = '/sap/bc/ui2/app_index/ui5_app_info?id=' + libs;
153
+ let url = '/sap/bc/ui2/app_index/ui5_app_info?id=' + encodeURIComponent(libs);
154
154
  const sapClient = urlParams.get('sap-client');
155
- if (sapClient?.length === 3) {
155
+ if (sapClient?.length === 3 && /^\d+$/.test(sapClient)) {
156
156
  url = url + '&sap-client=' + sapClient;
157
157
  }
158
158
  const response = await fetch(url);
@@ -185,9 +185,9 @@ export async function resetAppState(container: typeof sap.ushell.Container): Pro
185
185
  export async function registerComponentDependencyPaths(appUrls: string[], urlParams: URLSearchParams): Promise<void> {
186
186
  const libs = await getManifestLibs(appUrls);
187
187
  if (libs && libs.length > 0) {
188
- let url = '/sap/bc/ui2/app_index/ui5_app_info?id=' + libs;
188
+ let url = '/sap/bc/ui2/app_index/ui5_app_info?id=' + encodeURIComponent(libs);
189
189
  const sapClient = urlParams.get('sap-client');
190
- if (sapClient?.length === 3) {
190
+ if (sapClient?.length === 3 && /^\d+$/.test(sapClient)) {
191
191
  url = url + '&sap-client=' + sapClient;
192
192
  }
193
193
  const response = await fetch(url);
@@ -67,6 +67,8 @@ ADP_CREATE_CONTROLLER_EXTENSION_TITLE = Create Controller Extension
67
67
  ADP_CREATE_CONTROLLER_EXTENSION_DESCRIPTION = Controller extension with name ''{0}'' was created.
68
68
  ADP_ODATA_HEALTH_CHECK_TITLE = OData Service Health Check
69
69
  ADP_ODATA_SERVICE_DOWN_DESCRIPTION = The OData service with the {0} endpoint is down. Error: {1}.
70
+ ADP_ORPHANED_CHANGE_ERROR_TITLE = Missing File Detected
71
+ ADP_ORPHANED_FILE_DESCRIPTION = The "{0}" file referenced by the "{1}" change was not found. To resolve this error, delete the change file.
70
72
  ADP_ADD_ACTION_DIALOG_ACTION_ID_LABEL = Action ID
71
73
  ADP_ADD_ACTION_DIALOG_BUTTON_TEXT_LABEL = Button Text
72
74
  ADP_ADD_ACTION_DIALOG_HANDLER_METHOD_LABEL = Handler Method