Custom UI custom field inline edit in create issue dialog

Hello,
In approximately 2-3 weeks we’ll start the rollout of inline edit for Custom UI in create issue dialog for jira:customField and jira:customFieldType modules (the experience that already exists for UI Kit). This will result in Custom UI fields in edit state being no longer rendered in modal and instead they will be rendered directly in create issue dialog.
I will update the announcement once we start and complete the transition.

Edit: We had to delay the rollout of inline edit for Custom UI in create issue dialog. Once we’re ready to start the rollout of the feature we’ll let you know by writing another announcement about it.

4 Likes

we now see this in our first test instance. But now the values we enter are not stored anymore after the ticket was created.
Strange thing: we use UI Kit and had the modal dialog for this on the create till yesterday.
Since today our UI Kit fields do have inline edits, but they don’t work on the create issue screen.

Is there a known issue?
If you need more insight - just give me a note

Hi @oliver.straesser
Can you share how do your manifest.yml (without app id) and package.json files look like?

Edit: Also if you could share some video and even better a part of code you’re using (if you’re fine with sharing that) I’d appreciate it.

Sure @PawelRacki

And as I wrote: In the instances where the inline edit is not shown - everything works.
And we use for edit & view UI Kit

modules:
  jira:customFieldType:
    - key: bvsec-singleline
      name: ...
      description: ...
      icon: https://cdn-confidential-fields.aws.bitvoodoo.cloud/cf_text_field_single.png
      type: object
      formatter:
        expression: '`${value.placeholder}`'
      schema:
        properties:
          timestamp:
            type: number
          iv:
            type: string
          data:
            type: string
        required:
          - timestamp
      function: fieldView
      edit:
        function: fieldEdit
      contextConfig:
        function: configureContext
  webtrigger:
    - key: development-only
      function: development-onlyfunc
  trigger:
    - key: app-installed
      function: eventInstallApp
      events:
        - avi:forge:installed:app
        - avi:forge:upgraded:app
    - key: customfield-created
      function: customFieldCreated
      events:
        - avi:jira:created:field
    - key: customfield-updated
      function: customFieldUpdated
      events:
        - avi:jira:updated:field
    - key: event-customfield-trashed
      function: customFieldTrashed
      events:
        - avi:jira:trashed:field
    - key: customfield-restored
      function: customFieldRestored
      events:
        - avi:jira:restored:field
    - key: customfield-deleted
      function: customFieldDeleted
      events:
        - avi:jira:deleted:field
  function:
    - key: fieldView
      handler: index.fieldView
    - key: fieldEdit
      handler: index.fieldEdit
    - key: configureContext
      handler: index.runContextConfig
    - key: eventInstallApp
      handler: lifecycle.installApp
    - key: customFieldCreated
      handler: customfield-events.customFieldCreated
    - key: customFieldUpdated
      handler: customfield-events.customFieldUpdated
    - key: customFieldTrashed
      handler: customfield-events.customFieldTrashed
    - key: customFieldRestored
      handler: customfield-events.customFieldRestored
    - key: customFieldDeleted
      handler: customfield-events.customFieldDeleted
    - key: development-onlyfunc
      handler: webtrigger.developmentOnly
    - key: resolver-administration
      handler: administration.handler
  jira:adminPage:
    ...
resources:
  - key: resource-administration
    path: static/administration-subpages/build
app:
  id: #######
  runtime:
    snapshots: false
permissions:
  content:
    styles:
      - unsafe-inline
  external:
    fetch:
      backend:
        - '*.ngrok.io'
        - '*.aws.bitvoodoo.cloud'
      client:
        - '*.ngrok.io'
        - '*.aws.bitvoodoo.cloud'
    images:
      - '*.aws.bitvoodoo.cloud'
  scopes:
    - storage:app
    - read:field:jira
    - read:application-role:jira
    - read:group:jira
    - read:user:jira
    - read:avatar:jira
    - read:project-category:jira
    - read:project-role:jira
    - read:project:jira
    - read:issue-details:jira
    - read:field.default-value:jira
    - read:field.option:jira
    - read:jira-work

package.json

{
  "name": "...",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "private": true,
  "lint-staged": {
    "**/*": [
      "yarn pc",
      "yarn lint"
    ]
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "devDependencies": {
    "@types/jest": "28.1.1",
    "@types/jsonwebtoken": "8.5.8",
    "@types/node": "17.0.40",
    "@types/node-forge": "1.0.2",
    "@types/react": "18.0.12",
    "@types/react-dom": "18.0.5",
    "@types/react-router-dom": "5.3.3",
    "@types/uuid": "8.3.4",
    "@typescript-eslint/eslint-plugin": "5.29.0",
    "@typescript-eslint/parser": "5.29.0",
    "eslint": "8.18.0",
    "eslint-config-prettier": "8.5.0",
    "eslint-plugin-react-hooks": "2.1.2",
    "lint-staged": "13.0.3",
    "prettier": "2.7.1",
    "pretty-quick": "3.1.3",
    "tslint-config-prettier": "1.18.0",
    "typescript": "4.0.8"
  },
  "dependencies": {
    "@forge/api": "2.7.0",
    "@forge/cli": "4.5.0",
    "@forge/resolver": "1.4.4",
    "@forge/ui": "1.3.0",
    "@openapitools/openapi-generator-cli": "2.5.1",
    "crypto": "1.0.1",
    "deepmerge": "4.2.2",
    "dtsgenerator": "3.16.0",
    "husky": "8.0.1",
    "jsonwebtoken": "8.5.1"
  },
  "scripts": {
    "lint": "eslint ./**/*.ts{,x} --ext .ts",
    "hook-errors": "echo '\\x1b[31mThe build failed because a Forge UI hook is being used incorrectly. Forge UI hooks follow the same rules as React Hooks but have their own API definitions. See the Forge documentation for details on how to use Forge UI hooks.\n' && exit 1",
    "setup": "node --experimental-modules scripts/setup-dev-env.mjs",
    "build": "yarn --cwd './static/administration-subpages' run build",
    "installui": "yarn --cwd './static/administration-subpages'  install",
    "dev": "yarn tunnel",
    "tunnel": "yarn forge tunnel",
    "deploy": "yarn forge deploy",
    "install:forge": "yarn forge install",
    "dtsgen": "rm -rf ./src/jira-types/generated-types.d.ts; dtsgen -o ./src/jira-types/generated-types.d.ts --url https://developer.atlassian.com/cloud/jira/platform/swagger-v3.v3.json",
    "pc": "npx prettier --check .",
    "pw": "prettier --write .",
    "prepare": "husky install"
  }
}

Thanks @oliver.straesser
I’ll get back to you as soon as I find the root cause of this issue

@oliver.straesser could you please check if you (or maybe some of your coworkers?) bumped the @forge/ui package version recently? I believe it might be the reason why it transitioned from modal to inline edit. Also, could you show some simplified version of broken submit from your app so I could debug why the inline doesn’t work for you

we installed the same code version installed in the forge stage and forge dev env.
But the forge stage env is only installed in bitvoodoo-stage which is an EAP instance. And only there we do see the inline edit with the malfunction store behavior.

Yes, we bumped it. By cleaning node_modules I can reproduce the issue.
I just see that from my view.tx, the console.log is not printed - is there a change in processing?
Is useEffect and async functions not working anymore with inline edit in UI Kit?

Alright, so if the issue is really crucial for you right now, you can downgrade your forge/ui package by running npm i @forge/ui@1.1.0, however we still need need to figure out the reason why the app doesn’t work for you in inline edit mode in the first place. I’ve been trying to reproduce the broken behaviour by creating some object type custom field (looking at your manifest) but with no luck. It always works fine on my test instance. If you could write a single field app with reproduced bug and share it with us it would be very helpful. We can’t start debugging it till we have it reproduced and unfortunately I can’t find a way to reproduce it.

Also about the logs it should work fine. You can use forge logs command to have your console.logs printed. Here is a guide how to do it https://developer.atlassian.com/platform/forge/debugging/#logging

Thank you firstly for testing on your side too.
Downgrading works for the moment and unblocks the release :slight_smile:

But I think it would be cool if we can find anyway the root cause, because otherwise we can never upgrade the library :smiley:

Maybe the issue is that our async function gives trouble with forgu ui 1.3.0?

export const View = () => {
const productContext: IssueContext = useProductContext();
....
const [value, setValue] = useState<number | string | string[]>('');

useEffect(async () => {

   // in getData we read storage api and execute external calls
    const storedValue = await getData(productContext);

setValue(storedValue);
}
}, []);

We try to break it down and come back later.

Hi, @PawelRacki I do have reproducible code for you :smiley:! In fact, it’s the rerendering moment, when the field is leaving the focus and the value was processed.
After onSubmit:

  • The state is initialized again with the default values
  • useEffect is not executed again, so the state isn’t changed, which influences the field behavior.

My questions

  • Why is there a state reset that breaks everything or why is useEffect not called
  • Is this the desired behavior?
  • Do I have a misunderstanding?

Example: In my case, the field should be only shown, if a remote service says “yes”. So I ask for that service and change the state afterward (In the real example, the value from the field comes from the remote service too, but in our example here I removed that.)

Let’s have a lok into the log:

invocation: fa527f2b6e8b11f6 index.runEdit // Create Screen was opened, useEffect is called 
INFO    19:13:29.017  fa527f2b6e8b11f6  =======
INFO    19:13:29.022  fa527f2b6e8b11f6  permission false
INFO    19:13:29.024  fa527f2b6e8b11f6  useEffect
INFO    19:13:29.187  fa527f2b6e8b11f6  ok you are allowed to see the content
INFO    19:13:29.188  fa527f2b6e8b11f6  =======
INFO    19:13:29.188  fa527f2b6e8b11f6  permission false
INFO    19:13:29.189  fa527f2b6e8b11f6  =======
INFO    19:13:29.189  fa527f2b6e8b11f6  permission true

invocation: de2cbc86fc41bdeb index.runEdit // I entered a value and changed the field focus on create screen
INFO    19:13:39.346  de2cbc86fc41bdeb  =======
INFO    19:13:39.350  de2cbc86fc41bdeb  permission true
INFO    19:13:39.351  de2cbc86fc41bdeb  onSubmit
INFO    19:13:39.352  de2cbc86fc41bdeb  =======
INFO    19:13:39.352  de2cbc86fc41bdeb  permission true
INFO    19:13:39.353  de2cbc86fc41bdeb  =======
INFO    19:13:39.353  de2cbc86fc41bdeb  permission true

invocation: 7f0d863fc0edac8a index.runEdit // the field is rerendered - useEffect is not called
INFO    19:13:40.827  7f0d863fc0edac8a  =======
INFO    19:13:40.831  7f0d863fc0edac8a  permission false

Example code

import ForgeUI, {CustomFieldEdit, Text, TextField, useEffect, useProductContext, useState,} from '@forge/ui';
import {fetch} from '@forge/api';

const a = ForgeUI;

export const Edit = () => {
const {extensionContext: {fieldValue}} = useProductContext();
const [value, setValue] = useState('');
const [permission, setPermission] = useState(false);
console.log("=======")

useEffect(async () => {
console.log("useEffect")
// just a dummy request
const result = await fetch(
"https://jsonplaceholder.typicode.com/todos/1"
);

// based on the feedback of the endpoint we decide of the user is allowed to see the data or not
if (result.status === 200) {
console.log("ok you are allowed to see the content")
setValue(fieldValue);
setPermission(true);

}
}, []
);

const onSubmit = values => {
console.log("onSubmit")
setValue(values.text)
return values.text;

};

console.log("permission %s", permission);

if (!permission) {
return (
<CustomFieldEdit
onSubmit={onSubmit}
width='medium'
header='You cannot view or edit this data'
>
<Text>
A Jira administrator has restricted the data in this custom field. You
don’t have permission to view or edit it.
</Text>
</CustomFieldEdit>
);
} else {
return (
<CustomFieldEdit onSubmit={onSubmit}>
<TextField name="text" label="Say hello to:" defaultValue={value}/>
</CustomFieldEdit>
);
}
};

Whole project:

PS: I understood now, that the logs from the view never where shown, as the view is never in charge - it’s only the edit component - make sense :wink:

2 Likes

Hi @oliver.straesser
Thank you for the code. It will help me a lot :slight_smile:. I’ll investigate it as soon as i can and once I got some answers I’ll get back to you. For now, I’m glad that the downgrading forge/ui version unblocked your release.
Paweł

Hi again @oliver.straesser :wave:
I created a public ticket that you can watch to track the progress on fixing this issue: [FRGE-794] - Ecosystem Jira
Regards,
Paweł

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.

As announced in before the release of custom UI inline edit on create issue dialog is going to start in 2 weeks (28.09.2022). Here is an app example with the recommended way of submitting the data that will be used as a new template app for Forge custom UI custom field.

2 Likes