Last updated Jun 1, 2024

Build a custom Jira Dashboard Gadget

This tutorial will walk you through creating a more complex Forge app for Jira using UI Kit.

The Jira Weather Gadget App

The Jira Weather Gadget app is designed to show you how to create an app that allows a user to enter their location, and use that information to get data from an external API and display it.

Jira Weather Gadget App

About the tutorial

Approximate time needed: 30-40 minutes

Suggested Skills: Basic understanding of Javascript, React is recommended.

First, you'll create your Forge UI kit app using the Jira dashboard gadget template, you will customise the Configuration screen and display the configuration data in the app.

Then, you'll modify the app to call the OpenWeather API, and display the weather in your app. You will also learn how to store and access environment variables in Forge.

Finally you'll further customise the app to use UI Kit layout elements to improve the apps appearance.

Along the way, you'll find extra tips, links and resources that will allow you to learn more about what was covered in each section, and become familiar with how resources are laid out in the Forge documentation.

Before you begin

Before you start creating your app, ensure you've prepared by completing the steps outlined in Preparing for your first quest.

We recommend completing Forge Quest in order. If you haven't completed the first quest, it is recommended you complete that before starting this tutorial.

In this tutorial, we'll be using OpenWeatherMap API to get current weather conditions to display in our app. Sign up for a free account and generate your API key as you'll need them in this tutorial. You won't need a paid subscription for this tutorial.

Create your app

Create an app based on the Jira dashboard gadget template.

  1. Create your app by running:

    1
    2
    forge create
    
  2. Enter a name for your app (up to 50 characters). For example weather-gadget.

  3. Select the UI Kit category.

  4. Select Jira from the list of products.

  5. Select the jira-dashboard-gadget template.

  6. Change to the app subdirectory to see the app files:

    1
    2
    cd weather-gadget
    

About the jira-dashboard-gadget template

The app we'll create will create a Jira Dashboard Gadget, which can be added to the Dashboards page in Jira Work Management, Jira Software and Jira Service Management.

Deploy and install your app

  1. Navigate to the app's top-level directory and deploy your app by running:
    1
    2
    forge deploy
    
  2. Install your app by running:
    1
    2
    forge install
    
  3. Select jira using the arrow keys and press the enter key.
  4. Enter the URL for your Atlassian development site. For example example.atlassian.net. You can view a list of your active sites at Atlassian administration

Once the install complete message appears, your app is installed and ready to use on the site specified. You can delete your app from the site by running the forge uninstall command.

View your app

Now that your app is installed, it's time to see it in action in your Jira site.

  1. Open your jira site (example.atlassian.com) and click on the Dashboards menu to view a list of dashboards.
  2. Click on Create dashboard and follow the prompts to create a new dashboard for testing your app. Your new dashboard will be opened in Edit mode.
  3. In the Add a Gadget menu to the right of the screen search for your app using the title in the manifest.
  4. Click on the Add button for your gadget and your app will be added to the dashboard.
  5. The gadget will open in the Edit view, enter something (eg. hello jira) in the value box and click submit.
  6. Note the gadget will display the value you entered above 'Hello, world!' when in view mode.

Change the edit screen for your app

In this section, you'll make some changes to the src/frontend/index.jsx to modify the Edit view of the app to prompt the user to enter their Country and City, which you will need later to get the local weather information.

  1. Start your tunnel by running

    1
    2
    forge tunnel
    
  2. Navigate to the src/frontend directory.

  3. Open the index.jsx file. The default content of the file is shown below:

    1
    2
    import React, {useEffect, useState} from "react";
    import ForgeReconciler, {
      Text,
      useProductContext,
      Textfield,
      Form,
      Button,
      FormSection,
      FormFooter,
      Label,
      RequiredAsterisk,
      useForm,
    } from "@forge/react";
    import { invoke, view } from "@forge/bridge";
    const FIELD_NAME = "field-name";
    
    export const Edit = () => {
      const { handleSubmit, register, getFieldId } = useForm();
    
      const configureGadget = (data) => {
        view.submit(data);
      };
    
      return (
        <Form onSubmit={handleSubmit(configureGadget)}>
          <FormSection>
            <Label labelFor={getFieldId(FIELD_NAME)}>
              Value
              <RequiredAsterisk />
            </Label>
            <Textfield {...register(FIELD_NAME, { required: true })} />
          </FormSection>
          <FormFooter>
            <Button appearance="primary" type="submit">
              Submit
            </Button>
          </FormFooter>
        </Form>
      );
    };
    
    const View = () => {
      const [data, setData] = useState(null);
      const context = useProductContext();
    
      useEffect(() => {
        invoke('getText', { example: 'my-invoke-variable' }).then(setData);
      }, []);
    
      if (!context) {
        return "Loading...";
      }
      const {
        extension: { gadgetConfiguration },
      } = context;
    
      return (
        <>
          <Text>Value: {gadgetConfiguration[FIELD_NAME]}</Text>
          <Text>{data ? data : 'Loading...'}</Text>
        </>
      );
    };
    
    const App = () => {
      const context = useProductContext();
      if (!context) {
        return "Loading...";
      }
    
      return context.extension.entryPoint === "edit" ? <Edit /> : <View />;
    };
    
    ForgeReconciler.render(
      <React.StrictMode>
        <App />
      </React.StrictMode>
    );
    
    
  4. The Edit function returns a form with a textfield, label and submit button. Modify the textfield and label so they ask the user to enter their City,

    1
    2
     <Label>
       City
       <RequiredAsterisk />
     </Label>
     <Textfield {...register("city", { required: true })} />
    

    To learn more about UI kit Forms, you can consult the reference docs at https://developer.atlassian.com/platform/forge/ui-kit-2/heading/

  5. In the Edit function, below the City field, create a new textfield and label for the Country,

    1
    2
     <Label>
       Country
       <RequiredAsterisk />
     </Label>
     <Textfield {...register("country", { required: true })} />
    
  6. Modify the App return statement to display the city and country entered by the user,

    1
    2
     return (
       <>
         <Text>City: {gadgetConfiguration["city"] ? gadgetConfiguration["city"] : "Edit me"}</Text>
         <Text>Country: {gadgetConfiguration["country"] ? gadgetConfiguration["country"] : "Edit me"}</Text>
       </>
     );     
    
  7. Save the changes you made to index.jsx.

  8. Once the tunnel has completed its reload, refresh your Jira dashboard to see the changes.

  9. Click on the ... and select Configure to view the edit screen and enter your city and country.

    The ... option allowing you to configure your gadget will only appear when the dashboard is in Edit mode.

  10. Once you're happy with your changes, close the tunnel using Control + c and deploy your changes,

    1
    2
      forge deploy
    

Your src/frontend/index.jsx should look like this:

1
2
  import React, {useEffect, useState} from "react";
  import ForgeReconciler, {
    Text,
    useProductContext,
    Textfield,
    Form,
    Button,
    FormSection,
    FormFooter,
    Label,
    RequiredAsterisk,
    useForm,
  } from "@forge/react";
  import { invoke, view } from "@forge/bridge";

  export const Edit = () => {
    const { handleSubmit, register } = useForm();

    const configureGadget = (data) => {
      view.submit(data);
    };

    return (
      <Form onSubmit={handleSubmit(configureGadget)}>
        <FormSection>
          <Label>
            City
            <RequiredAsterisk />
          </Label>
          <Textfield {...register("city", { required: true })} />
          <Label>
          Country
          <RequiredAsterisk />
        </Label>
        <Textfield {...register("country", { required: true })} />
        </FormSection>
        <FormFooter>
          <Button appearance="primary" type="submit">
            Submit
          </Button>
        </FormFooter>
      </Form>
    );
  };

  const View = () => {
    const [data, setData] = useState(null);
    const context = useProductContext();

    useEffect(() => {
      invoke('getText', { example: 'my-invoke-variable' }).then(setData);
    }, []);

    if (!context) {
      return "Loading...";
    }
    const {
      extension: { gadgetConfiguration },
    } = context;

    return (
      <>
        <Text>City: {gadgetConfiguration["city"] ? gadgetConfiguration["city"] : "Edit me"}</Text>
        <Text>Country: {gadgetConfiguration["country"] ? gadgetConfiguration["country"] : "Edit me"}</Text>
      </>
    );
  };

  const App = () => {
    const context = useProductContext();
    if (!context) {
      return "This is never displayed...";
    }

    return context.extension.entryPoint === "edit" ? <Edit /> : <View />;
  };

  ForgeReconciler.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  );

Next step

In the next step you will modify the app to call the OpenWeather API, and display the weather in your app. You will also learn how to store and access environment variables in Forge.

Rate this page: