Last updated Sep 24, 2024

Creating an Extension

This is a continuation of the work started in Getting Started.

In this first section, you will:

  • Learn about extension factories and how to use them to create extensions.
  • Learn how to register your extensions in your Atlassian Plugin with [CSE webpack plugin] and comment annotation.
  • Create Link and Button extensions.
  • Get an overview of how products consume your extensions.

Extension factories

Client-side Extensions (CSE) provides a set of helpers called Extension factories. There is a factory for each extension type supported, and they will help you create extensions by providing better IDE support and TypeScript type definitions of the APIs you will consume.

You can install them via npm:

1
2
npm install --save @atlassian/clientside-extensions

If you're following the introduction using the Bitbucket CSE Template, this will come pre-installed for you.

Let's explore the basics of CSE by creating the most simple extension: a Link.

Links let you redirect users to a different section or page of the application, or redirect them to an external site. They usually consist of a label and a URL.

Start by creating a file for your extension in your working directory and use the LinkExtension factory to define a Link as follows:

  1. Create a file in /src/my-app/extensions/ called first-extension.js.
  2. In that file, import LinkExtension from @atlassian/clientside-extensions:
1
2
// #/src/my-app/extensions/first-extension.js
import { LinkExtension } from '@atlassian/clientside-extensions';
  1. Use the Link extension factory to declare a link with a label and a URL:
1
2
// #/src/my-app/extensions/first-extension.js
import { LinkExtension } from '@atlassian/clientside-extensions';

export default LinkExtension.factory(() => {
    return {
        label: 'Extensions are awesome!',
        url: 'https://go.atlassian.com/clientside-extensions',
    };
});

You can learn all about Link extensions in our API Reference

Attributes

Each factory receives a callback as an argument. This callback should always return an object with key:value pairs called attributes.

Attributes will be picked up by products and used to render your extension. In this case, you're specifying a label for your link and a URL where the user will be redirected when clicked. Bitbucket is using that information to render your link.

Always remember to check the documentation of each product's extension point and supported attributes.

You will read more information about revealing extension points on the page in the next part of the guide.

Comment annotations

After defining your extension as a link with LinkExtension, you need to register it as part of your Atlassian Plugin.

CSE makes use of comment annotations to mark your code as a Client-side Extension. These annotations are then processed by the CSE webpack plugin, and will use this information to create all the Atlassian Plugin configuration for you.

In /src/my-app/extensions/first-extension.js file:

  1. Mark your default export as a Client-side Extension.

    It's important to note that there can only be one extension per file, since the webpack plugin will only look for the default exports.

  2. Specify the location where you want to render your link extension. In this case, that will be the Pull Request Overview summary extension point.

1
2
// #/src/my-app/extensions/first-extension.js
import { LinkExtension } from '@atlassian/clientside-extensions';

/**
 * @clientside-extension
 * @extension-point bitbucket.ui.pullrequest.overview.summary
 */
export default LinkExtension.factory(() => {
    return {
        label: 'Extensions are awesome!',
        iconBefore: 'app-access',
        url: 'https://go.atlassian.com/clientside-extensions',
    };
});

You can learn all about annotations in our metadata reference

Testing your extension

After saving the file, go to the testing pull request you created in the getting started section and you should see your first extension. Clicking the link should take you back to CSE documentation.

If you can't see it, restart the CSE watch server and try again.

You can also open the console and check for a useful error thrown by the Client-side Extensions system, and some hints on how to solve it.

From here on, every time you make a change to your extension, webpack should recompile your code and the changes will be available after refreshing the page.

Executing an action on click

So far, you've created a link to redirect users to a given page. But what if you want to perform some action when the user clicks your element?

In that case, what you need is to create a Button extension.

Creating a button

Using ButtonExtension factory, you can specify that you want to create an extension that then executes some arbitrary JavaScript code when clicked.

In the /src/my-app/extensions/first-extension.js file, change your link to be a button instead:

  1. Import ButtonExtension from @atlassian/clientside-extensions:
1
2
// #/src/my-app/extensions/first-extension.js
import { ButtonExtension } from '@atlassian/clientside-extensions';
  1. Use ButtonExtension.factory instead of the link factory.
  2. Remove the url attribute. Buttons don't support URLs.
  3. Finally, let's use a special attribute called onAction and assign a function that will be called when the button is clicked.
1
2
// #/src/my-app/extensions/first-extension.js
import { ButtonExtension } from '@atlassian/clientside-extensions';

/**
 * @clientside-extension
 * @extension-point bitbucket.ui.pullrequest.overview.summary
 */
export default ButtonExtension.factory(() => {
    return {
        label: 'Extensions are awesome!',
        iconBefore: 'app-access',
        onAction: () => {
            alert('They are awesome indeed!');
        },
    };
});

Save your changes and refresh your pull request. You should see an element with the text "Extensions are awesome" that, when clicked, executes your alert code.

You now have a working button!

onAction

Extensions have a special attribute called onAction. Its behavior is different for each type of extension.

In the case of a button, the function assigned to onAction will be the callback executed when the user clicks the button.

You can learn all about Button extensions in our API reference

Limitations of extensions

A button extension doesn't create a "button" element on the page, but instead communicates to the product that you would like to render a button in a location. The product is ultimately responsible for the location on the page where that button is rendered, and the appearance of that button. The same applies to all types of extensions.

In your button extension, Bitbucket has decided that both links and buttons should look the same in that location.

Extensions are about declaring what your feature does so that the product can represent it in the UI in the most appropriate way.

Adding an icon to your button

Products can provide ways for you to modify some parts of the appearance or behavior of the extensions. In this location, Bitbucket allows you to specify an icon to use before the label of your button by using an attribute called iconBefore.

In this attribute, you can specify the name of an Atlaskit icon and the product will render it for you.

In the /src/my-app/extensions/first-extension.js file, add an icon to your button:

  1. Add a new attribute to your extension factory called iconBefore.
  2. Assign the value app-access.
1
2
// #/src/my-app/extensions/first-extension.js
import { ButtonExtension } from '@atlassian/clientside-extensions';

/**
 * @clientside-extension
 * @extension-point bitbucket.ui.pullrequest.overview.summary
 */
export default ButtonExtension.factory(() => {
    return {
        label: 'Extensions are awesome!',
        iconBefore: 'app-access',
        onAction: () => {
            alert('They are awesome indeed!');
        },
    };
});

Save and refresh, and you should see an icon with your button.

In the next section, you will learn how to discover all the possible attributes you can use in a given extension point.

Recap and next steps

So far, you've learned:

  • How to create an extension with factories.
  • How to register an extension to be rendered with annotations.
  • How to use onAction to execute some JavaScript code when a user clicks your buttons.
  • That you can influence how your extensions are rendered by using extra attributes.

Next, you're going to learn how to discover extension points.

Rate this page: