Rate this page:
The jira:uiModifications
module allows you to change the look and behavior of the Global issue create (GIC) and Issue view
when used in conjunction with the UI modifications (apps) REST API.
This module can be used in Jira Software only.
UI modifications (UIM) is a runtime extension which allows applications to modify the UI on supported screens in a given UIM app mounting context (for example, a combination of a project and an issue type for GIC or Issue view). The UI is modified using the UIM JS API in Forge applications in the UIM Forge module. You can manage the available UI modifications and their contexts using the UIM REST API. Applications can store additional data related to UI modifications as UIM data, which is also managed by the UIM REST API. Each UI modification is backed by a UIM entity which represents it on the back-end and is delivered to the front-end in UIM data.
To understand the broader context of this module read the UI modifications guide.
An Atlassian Forge application which uses the UIM Forge module. A single UIM app can declare only one UIM Forge module.
Provided to the UIM app by Jira. It consists of a project, issueType, and uiModifications (UIM data).
A combination of a project
, issueType
, and viewType
.
UIM supports the following view types:
An array of UIM entities for a given UIM app invocation context. The interpretation of UIM data is the responsibility of the UIM app. UIM data can be accessed through the invocation argument within the initCallback
and the changeCallback
.
A single mapping of custom textual data and a UIM app mounting context. It can be created and obtained using the UIM REST API.
The API provided to the UIM app through the @forge/jira-bridge
module. For more details, see the Jira Bridge uiModifications documentation.
A UI modifications Forge module (jira:uiModifications
) declared in the manifest.
The back-end REST API used to assign and retrieve specific data (related to project, issueType, and viewType) to be consumed by the UIM app through the UIM app invocation context. For more details, see the UI modifications (apps) documentation.
To write your first UIM app, follow the detailed instructions below or try out the example app at atlassian/forge-ui-modifications-example.
You can build your Forge app following one of our guides. To create an app that will run as a UIM app, make sure to update your manifest.yml file to include:
1 2modules: jira:uiModifications: - key: ui-modifications-app title: Example UI modifications app resource: uiModificationsApp resources: - key: uiModificationsApp path: static/ui-modifications/dist
UIM apps depend on the @forge/jira-bridge
package which exposes the uiModifications API from version 0.6.0 onwards.
Both initialization-triggered and user-triggered phase changes are supported.
This package works on the client side and will be effective only when used within the static resource declared in the jira:uiModifications
module.
The UIM Forge module will be rendered inside an invisible iframe
element. The only meaningful part is the script linked in the main HTML file.
1 2//static/hello-world/index.js import { uiModificationsApi } from '@forge/jira-bridge'; uiModificationsApi.onInit( ({ api }) => { const { getFieldById } = api; // Hiding the priority field const priority = getFieldById('priority'); priority?.setVisible(false); // Changing the summary field label const summary = getFieldById('summary'); summary?.setName('Modified summary label'); // Changing the assignee field description const assignee = getFieldById('assignee'); assignee?.setDescription('Description added by UI modifications'); // Get value of labels field const labels = getFieldById('labels'); const labelsData = labels?.getValue() || []; labels?.setDescription( `${labelsData.length} label(s) are currently selected` ); }, () => ['priority', 'summary', 'assignee', 'labels'] );
Keep in mind that all changes requested during the run of the onInit
callback will be applied at once after the function completes its execution.
The execution of changes can be postponed, for example when a UIM needs to perform some async operations before evaluating which modification to apply. For that purpose, our API allows you to return a promise
object from the callback. The changes will be postponed until the promise
resolves.
A correct implementation using a promise
:
1 2import { uiModificationsApi } from '@forge/jira-bridge'; // Below: imaginary import import { shouldPriorityBeHidden } from '../my-services'; uiModificationsApi.onInit( ({ api }) => { const { getFieldById } = api; // Hiding the priority field const priority = getFieldById('priority'); // We store the update Promise const priorityUpdate = shouldPriorityBeHidden().then((result) => { if (result === true) { priority?.setVisible(false); } }); // Changing the assignee field description const assignee = getFieldById('assignee'); assignee?.setDescription('Description added by UI modifications'); // We return the promise. In this case even the assignee field description // will be updated only after the priorityUpdate promise resolves. return priorityUpdate; }, () => ['priority', 'assignee'] );
Note that async/await
syntax is supported:
1 2import { uiModificationsApi } from '@forge/jira-bridge'; // Below: imaginary import import { shouldPriorityBeHidden } from '../my-services'; uiModificationsApi.onInit( async ({ api }) => { const { getFieldById } = api; // Hiding the priority field const priority = getFieldById('priority'); const result = await shouldPriorityBeHidden(); if (result === true) { priority?.setVisible(false); } // Changing the assignee field description const assignee = getFieldById('assignee'); assignee?.setDescription('Description added by UI modifications'); }, () => ['priority', 'assignee'] );
The UIM Forge module will only render when configured for a given projectId
and issueTypeId
using the UIM REST API.
For example, there could be an AdminPage that creates a UIM using the REST API. Apps can also include custom UIM data to be passed to the UIM Forge module when it is executed, for example a list of rules that a user has selected:
1 2import api, { route } from '@forge/api'; const createUiModification = async (projectId, issueTypeId) => { const result = await api.asApp().requestJira(route`/rest/api/3/uiModifications`, { method: "POST", body: JSON.stringify({ name: 'demo-ui-modification', data: '["custom data", "for your app"]', contexts: [ { projectId, issueTypeId, viewType: 'GIC' }, { projectId, issueTypeId, viewType: 'IssueView' } ], }), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, }); console.log(`Created UI modification with status ${result.status}`); return result.status; }
This custom UIM data is available as uiModifications
within the onInit
and onChange
callbacks. It has the following shape:
1 2uiModifications: Array<{ id: string, data?: string, }>
Property | Type | Required | Description |
---|---|---|---|
key |
| Yes |
A key for the module, which other modules can refer to. Must be unique within the manifest. Regex: |
resource | string | Yes |
A reference to the static resources HTML file entry that should be loaded in the invisible iframe on GIC or Issue view.
See resources for more details. |
title | string | Yes | A title for the module. |
resolver | { function: string } | Contains a function property, which references the function module that defines the configuration of resource . Can only be set if the module is using the resource property. |
UIM Forge modules can retrieve the current project
and issueType
by using the view.getContext()
method exposed by the @forge/bridge
module - see custom UI view.
The UIM context shape on GIC is:
1 2{ extension: { type: 'jira:uiModifications', project: { id: string, key: string, type: string, }, issueType: { id: string, name: string, }, viewType: 'GIC' } }
The UIM context shape on Issue view is:
1 2{ extension: { type: 'jira:uiModifications', project: { id: string, key: string, type: string, }, issueType: { id: string, name: string, }, issue: { id: string, key: string, } viewType: 'IssueView' } }
Usage:
1 2import { view } from '@forge/bridge'; import { uiModificationsApi } from '@forge/jira-bridge'; uiModificationsApi.onInit(async ({ api, uiModifications }) => { const { getFieldById } = api; const context = await view.getContext(); const { project, issueType } = context.extension; uiModifications.forEach(({ data: customDataConfiguredUsingRestApi }) => { // ... }); }, ({ uiModifications }) => [ // ... ])
UI modifications expose customer data to the app that provides them. Therefore, you must declare either classic (recommended) or granular scopes in your manifest. Note that you always have to declare all scopes from your chosen group.
If user consent isn’t given for the scopes mentioned inside the app’s manifest, UIM won’t render (same as with any other extension point). Users will be prompted to give consent when they open GIC in a project/issue type that has been configured with a UIM. For the Issue view, the consent dialog is presented at the end of the page and doesn’t block the user from interacting with the view.
User consent applies for all modules in the app, so consent previously given in an admin page will be valid elsewhere. Additionally, if a user does not give consent, they will be prompted again the next time the UI modification is loaded. To read more about scopes, see Permissions.
1 2permissions: scopes: - 'read:jira-user' - 'read:jira-work' - 'manage:jira-configuration' - 'write:jira-work'
Scope | Data exposed | Field | Method | ||
---|---|---|---|---|---|
read:jira-user | User timezone and account ID | n/a | view.getContext | ||
User display name, account ID, and avatar | Assignee | getValue | |||
read:jira-work | Project ID, key, and type; issue type ID and issue type name of the issue being created using the GIC form or presented on the Issue view | n/a | view.getContext | ||
Issue data of the issue being created using the GIC form or presented on the Issue view | All supported fields | getValue | |||
Field name | All supported fields | getName | |||
Field visibility | All supported fields | isVisible | |||
manage:jira-configuration | Field description | All supported fields | getDescription | ||
User locale | n/a | view.getContext | |||
Product license status | n/a | view.getContext | |||
The following values can be modified:
| All supported fields |
| |||
write:jira-work | Default field value can be modified | All supported fields |
|
1 2permissions: scopes: - 'read:project:jira' - 'read:issue-type:jira' - 'read:user:jira' - 'read:user-configuration:jira' - 'read:issue:jira' - 'read:priority:jira' - 'read:label:jira' - 'read:project.component:jira' - 'read:project-version:jira' - 'read:license:jira' - 'read:field:jira' - 'read:field-configuration:jira' - 'write:field-configuration:jira' - 'write:field.default-value:jira' - 'write:field:jira'
Scope | Data exposed | Field | Method |
---|---|---|---|
read:project:jira | Project ID, key, and type of the issue being created using the GIC form or presented on the Issue view | n/a | view.getContext |
read:issue-type:jira | Issue type ID and issue type name of the issue being created using the GIC form or presented on the Issue view | n/a | view.getContext |
read:user:jira | User account ID | n/a | view.getContext |
User account ID | Assignee, Reporter, People, User picker, Multiple user picker | getValue | |
read:user-configuration:jira | User timezone and locale | n/a | view.getContext |
read:issue:jira | Issue data of the issue being created using the GIC form or presented on the Issue view | All supported fields | getValue |
read:priority:jira | Priority value | Priority | getValue |
read:label:jira | Label value | Labels | getValue |
read:project.component:jira | Components value | Components | getValue |
read:project-version:jira | Fix versions value | Fix versions | getValue |
read:license:jira | Product license status | n/a | view.getContext |
read:field:jira | Field name | All supported fields | getName |
read:field-configuration:jira | Field description | All supported fields | getDescription |
Field visibility | isVisible | ||
write:field-configuration:jira | Field description can be modified | All supported fields | setDescription |
Field visibility can be modified | setVisible | ||
write:field.default-value:jira | Default field value can be modified | All supported fields | setValue |
write:field:jira | Field name can be modified | All supported fields | setName |
The following scopes need to be in the app manifest only if the app will assign a UI modification to Issue view.
Scope | Data exposed | Field | Method |
---|---|---|---|
write:issue:jira | Issue field values can be modified | All supported fields on Issue view | setValue |
read:issue-field-values:jira | Issue field values can be read | All supported fields on Issue view | getValue |
In case a required permission isn’t assigned, the user will see the following error:
1 2We couldn't load the UI modifications configuration for this form
If you install and configure multiple UIM apps to run for a given combination of project, issue type, and view type, only the one that created a UI modification the earliest will run. Uninstalling an app doesn’t remove its configuration. This means that if you reinstall an app that was configured earlier than the one that’s currently active, it will take over.
UIM for Global issue create currently support the following project types:
The GIC can be opened from many places in the system. We currently support the following entry points:
c
keyboard shortcutCreateIssueModal
from @forge/jira-bridge
UIM are loaded after the Create issue dialog has finished loading. The user is informed about the loading state by a small spinner icon next to the label, which indicates that a UIM is running. Users can still see the fields before the modifications are applied. For example, a field will be visible for a moment before being hidden, or the default field description will be visible before it changes.
Fields can be hidden by individual users using Show fields. Data for these hidden fields is not sent to the UIM app.
Find your field does not know about fields being hidden by a UIM app using setVisible
. Users may not be able to discover why a field is not visible to them.
This page describes a Forge preview feature. Preview features are deemed stable; however, they remain under active development and may be subject to shorter deprecation windows. Preview features are suitable for early adopters in production environments.
We release preview features so partners and developers can study, test, and integrate them prior to General Availability (GA). For more information, see Forge release phases: EAP, Preview, and GA.
UIM for Issue view currently support the following project types:
The Issue view can be displayed in many places in Jira. We currently support the following entry points:
Because by default fields are in read mode, when your app uses the setValue
method, the changes are automatically committed and persisted to the database. Calling setValue doesn’t enable edit mode. Customizations applied using other methods are only valid for the current user session.
Because many users may be viewing an issue at the same time, setValue
will normally trigger an issue data refresh and show the new value to all users. However, an issue data refresh doesn’t trigger when only the user making the change is viewing the issue.
The consent dialog is presented at the end of the page and doesn’t block the user from interacting with the view. It might not be visible without scrolling depending on your browser resolution.
The issue view updates data optimistically and may process invalid input from the user before rolling back the changes.
If that happens, UIM apps won’t call a second onChange
and may unsync from the current state of the Issue view.
For example, if a user updates the Summary field to contain 240 characters, and then updates it again to contain 260 characters, this will trigger a validation error and the value will be rolled back to 240 characters. Your UIM app will receive both changes, but won’t receive the rollback.
UI modifications are loaded after the Issue view has finished loading. For example, a field will be visible for a moment before being hidden.
There are no callbacks available to react to real time updates in the Issue view.
Rate this page: