Repository: robocorp/robocorp (language: python)

Issue:
Action Server UI does not always show the argument names
### Target components

action_server

### What is wrong

The input argument names are not always visible in the action UI.

1. Take the template action with `action-server new`
2. `action-server start`
3. Open the Action Server UI
4. Go to run the action
-> The argument names `user_timezone` and `compare_to_timezones` are not visible.

There is something random at play here because this state was somehow triggered by adding default values for the input arguments. After running that once the UI stopped showing the arg names.
The other image below shows a case where only the first argument name is missing.

![image](https://github.com/robocorp/robocorp/assets/56814402/a380db4d-b526-4775-8d03-f40ad3d45227)
![image](https://github.com/robocorp/robocorp/assets/56814402/f964b1f2-7248-490c-ac1b-5c8de8fa4afe)


### System info

Happened on Windows and mac.
Template action project
Action Server v0.3.2

### Example script

_No response_


Discussion / hints:

Fix patch:
diff --git a/action_server/docs/CHANGELOG.md b/action_server/docs/CHANGELOG.md
index f7c3a3f3..95ed9a90 100644
--- a/action_server/docs/CHANGELOG.md
+++ b/action_server/docs/CHANGELOG.md
@@ -8,6 +8,9 @@
 - RCC updated to `v17.23.2`.
 - RCC `--bundled` flag now passed when called from the action server.
 - When the help is shown the action server version is shown in the description.
+- Action Server Builtin UI:
+    - Fix issue where the label for some entry would not be shown in the run. [#370](https://github.com/robocorp/robocorp/issues/370)
+    - Show ` (item)` when editing an item from a list.
 
 ## 0.3.2 - 2024-04-12
 
diff --git a/action_server/frontend/src/lib/formData.ts b/action_server/frontend/src/lib/formData.ts
index ef2f71aa..45edec50 100644
--- a/action_server/frontend/src/lib/formData.ts
+++ b/action_server/frontend/src/lib/formData.ts
@@ -35,6 +35,24 @@ const getDefaultValue = (property: OpenAPIV3_1.SchemaObject): PropertyFormDataTy
   }
 };
 
+/**
+ * Sets the title for one item of a list (i.e.: each item of a list
+ * should not have the same title as the list itself so that it's
+ * clearer what's an item of a list vs the list itself).
+ *
+ * @param item The item which should have the title set.
+ */
+export const setArrayItemTitle = (item: PropertyFormData) => {
+  let newTitle = item.property.title;
+  if (newTitle.endsWith('*')) {
+    newTitle = newTitle.substring(0, newTitle.length - 1);
+  }
+  if (!newTitle.endsWith(' (item)')) {
+    newTitle += ' (item)';
+  }
+  item.property.title = newTitle; // eslint-disable-line no-param-reassign
+};
+
 export const propertiesToFormData = (
   schema: OpenAPIV3_1.BaseSchemaObject,
   parents: string[] = [],
@@ -113,6 +131,7 @@ export const propertiesToFormData = (
           value: getDefaultValue(property.items),
         };
         rowEntry.value = [rowProperty];
+        setArrayItemTitle(rowProperty);
 
         return [rowEntry, rowProperty];
       }
diff --git a/action_server/frontend/src/views/actions/components/ActionRun.tsx b/action_server/frontend/src/views/actions/components/ActionRun.tsx
index 5e5f619a..e6e82d8c 100644
--- a/action_server/frontend/src/views/actions/components/ActionRun.tsx
+++ b/action_server/frontend/src/views/actions/components/ActionRun.tsx
@@ -14,7 +14,7 @@ import {
 import { IconBolt } from '@robocorp/icons/iconic';
 
 import { Action, ActionPackage } from '~/lib/types';
-import { toKebabCase } from '~/lib/helpers';
+import { logError, toKebabCase } from '~/lib/helpers';
 import { useActionServerContext } from '~/lib/actionServerContext';
 import { useLocalStorage } from '~/lib/useLocalStorage';
 import {
@@ -24,6 +24,7 @@ import {
   propertiesToFormData,
   PropertyFormData,
   PropertyFormDataType,
+  setArrayItemTitle,
 } from '~/lib/formData';
 import { useActionRunMutation } from '~/queries/actions';
 import { Code } from '~/components/Code';
@@ -79,13 +80,11 @@ const asFloat = (v: string) => {
   // For the time being this is a limitation of the current approach
   // (so, it's possible that a Nan is generated here and then
   // the handleInputChange needs to guard against it).
-  let ret = parseFloat(v);
-  return ret;
+  return parseFloat(v);
 };
 
 const asInt = (v: string) => {
-  const ret = parseInt(v, 10);
-  return ret;
+  return parseInt(v, 10);
 };
 
 export const ActionRun: FC<Props> = ({ action, actionPackage }) => {
@@ -103,7 +102,7 @@ export const ActionRun: FC<Props> = ({ action, actionPackage }) => {
 
   const handleInputChange = useCallback((value: PropertyFormDataType, index: number) => {
     if (typeof value === 'number') {
-      if (isNaN(value)) {
+      if (Number.isNaN(value)) {
         // i.e.: this means the user entered a bad value. Don't enter it as the roundtrip
         // would make the user loose the current value up to this point.
         return;
@@ -131,10 +130,12 @@ export const ActionRun: FC<Props> = ({ action, actionPackage }) => {
         const suffix = item.name.split('.').slice(depth).join('.');
         const newName = `${curr[index].name}.${newIndex}${suffix ? `.${suffix}` : suffix}`;
 
-        return {
+        const ret = {
           ...item,
           name: newName,
         };
+        setArrayItemTitle(item);
+        return ret;
       });
 
       let indexAt = index + 1;
@@ -163,6 +164,7 @@ export const ActionRun: FC<Props> = ({ action, actionPackage }) => {
         args = useRawJSON ? JSON.parse(formRawJSON) : formDataToPayload(formData);
       } catch (err) {
         // TODO: Use some better component from the design system.
+        // eslint-disable-next-line no-alert
         alert(
           'Unable to run action because the input is not valid (the input cannot be converted to JSON).',
         );
@@ -171,7 +173,7 @@ export const ActionRun: FC<Props> = ({ action, actionPackage }) => {
       runAction({
         actionPackageName: toKebabCase(actionPackage.name),
         actionName: toKebabCase(action.name),
-        args: args,
+        args,
         apiKey: serverConfig?.auth_enabled ? apiKey : undefined,
       });
     },
@@ -199,7 +201,9 @@ export const ActionRun: FC<Props> = ({ action, actionPackage }) => {
     if (!useRawJSON) {
       try {
         setFormData(payloadToFormData(JSON.parse(formRawJSON), formData));
-      } catch (err) {}
+      } catch (err) {
+        logError(err);
+      }
     } else {
       try {
         if (formData.length === 0) {
@@ -208,7 +212,9 @@ export const ActionRun: FC<Props> = ({ action, actionPackage }) => {
           const payload = formDataToPayload(formData);
           setFormRawJSON(JSON.stringify(payload, null, 4));
         }
-      } catch (err) {}
+      } catch (err) {
+        logError(err);
+      }
     }
   }, [useRawJSON]);
 
@@ -319,7 +325,7 @@ export const ActionRun: FC<Props> = ({ action, actionPackage }) => {
                 );
               case 'array':
                 return (
-                  <ItemArray title={item.title} name={item.name} key={item.name}>
+                  <ItemArray title={title} name={item.name} key={item.name}>
                     <Button type="button" onClick={() => onAddRow(index)} size="small">
                       Add row
                     </Button>
@@ -330,8 +336,8 @@ export const ActionRun: FC<Props> = ({ action, actionPackage }) => {
                 return (
                   <Item title={item.title} name={item.name} key={item.name}>
                     <Input
-                      key={`${item.title}-${item.name}-${index}`}
-                      label={item.title}
+                      key={`${title}-${item.name}-${index}`}
+                      label={title}
                       description={item.property.description}
                       rows={1}
                       required={item.required}


Test patch:
diff --git a/action_server/frontend/src/__tests/fixtures/case2FormData.json b/action_server/frontend/src/__tests/fixtures/case2FormData.json
index dca74bd8..93d8aea5 100644
--- a/action_server/frontend/src/__tests/fixtures/case2FormData.json
+++ b/action_server/frontend/src/__tests/fixtures/case2FormData.json
@@ -13,7 +13,7 @@
     "value": [
       {
         "name": "data.rows.0",
-        "property": { "title": "Rows", "description": "", "type": "integer" },
+        "property": { "title": "Rows (item)", "description": "", "type": "integer" },
         "required": true,
         "value": 0
       }
@@ -21,7 +21,7 @@
   },
   {
     "name": "data.rows.0",
-    "property": { "title": "Rows", "description": "", "type": "integer" },
+    "property": { "title": "Rows (item)", "description": "", "type": "integer" },
     "required": true,
     "value": 0
   },
diff --git a/action_server/frontend/src/__tests/fixtures/formData.json b/action_server/frontend/src/__tests/fixtures/formData.json
index acb0a987..8e96d635 100644
--- a/action_server/frontend/src/__tests/fixtures/formData.json
+++ b/action_server/frontend/src/__tests/fixtures/formData.json
@@ -86,7 +86,7 @@
             "name": "data.education.rows.0.columns.0",
             "property": {
               "description": "List of Education Properties",
-              "title": "Columns",
+              "title": "Columns (item)",
               "type": "string"
             },
             "required": true,
@@ -98,7 +98,7 @@
         "name": "data.education.rows.0.columns.0",
         "property": {
           "description": "List of Education Properties",
-          "title": "Columns",
+          "title": "Columns (item)",
           "type": "string"
         },
         "required": true,
@@ -120,7 +120,7 @@
         "name": "data.education.rows.0.columns.0",
         "property": {
           "description": "List of Education Properties",
-          "title": "Columns",
+          "title": "Columns (item)",
           "type": "string"
         },
         "required": true,
@@ -132,7 +132,7 @@
     "name": "data.education.rows.0.columns.0",
     "property": {
       "description": "List of Education Properties",
-      "title": "Columns",
+      "title": "Columns (item)",
       "type": "string"
     },
     "required": true,
diff --git a/action_server/frontend/src/__tests/formData.test.ts b/action_server/frontend/src/__tests/formData.test.ts
index eaf647a4..2e1391bf 100644
--- a/action_server/frontend/src/__tests/formData.test.ts
+++ b/action_server/frontend/src/__tests/formData.test.ts
@@ -54,7 +54,7 @@ test('Payload to Form Data, Case 2', () => {
       value: [
         {
           name: 'data.rows.0',
-          property: { title: 'Rows', description: '', type: 'integer' },
+          property: { title: 'Rows (item)', description: '', type: 'integer' },
           required: true,
           value: 0,
         },
@@ -62,13 +62,13 @@ test('Payload to Form Data, Case 2', () => {
     },
     {
       name: 'data.rows.0',
-      property: { title: 'Rows', description: '', type: 'integer' },
+      property: { title: 'Rows (item)', description: '', type: 'integer' },
       required: true,
       value: 3,
     },
     {
       name: 'data.rows.1',
-      property: { title: 'Rows', description: '', type: 'integer' },
+      property: { title: 'Rows (item)', description: '', type: 'integer' },
       required: true,
       value: 4,
     },