This is a continuation of the work started in 2. Discovering extension points.
In this section, you will learn how to:
The Client-side Extensions (CSE) runtime provides an API to interact with products. Your extensions will receive it as the first parameter of your extension factory. For more info, see Extensions API.
To use the Extensions API:
/src/my-app/extensions/first-extension.js
file.extensionAPI
object as the first parameter.console.log(extensionAPI)
to explore the methods you have available.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((extensionAPI) => { return { label: 'Extensions are awesome!', iconBefore: 'app-access', onAction: () => { console.log(extensionAPI); }, }; });
You should see an object in the console with the methods available for you to use.
In the majority of cases, you'd like to create extensions that do more than just alert messages. For example, you might want to create an extension that tracks how many times someone clicked it and updates its label after every click.
To do so, you can use updateAttributes
to choose a set of attributes to update. The CSE runtime will notify the product about
your changes and schedule a re-render of your extension with the new information.
It's important to understand that updateAttributes
is scheduling a re-render instead of rendering your extension right away. This is to avoid performance issues with multiple extensions rendering at the same time.
Now, change the logic of your Button extension to keep track of the number of times clicked and show it to the user.
In the /src/my-app/extensions/first-extension.js
file:
onAction
method to increment the count.onAction
method to request an update of your label attribute with the new count of times 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((extensionAPI) => { let clicked = 0; return { label: `Go ahead, click me!`, iconBefore: 'app-access', onAction: () => { // increment times clicked clicked++; // update label with times clicked extensionAPI.updateAttributes({ label: `Clicked: ${clicked} times. Amazing!`, }); }, }; });
You don't need to set all attributes again, just the ones you want to update.
If your extension needs to listen to an event or subscribe to a stream of information, it’s a good practice to clear these connections once the extension is removed from the page. This will help you avoid memory leaks.
You can do it by using onCleanup
- an API that lets you specify the cleanup logic to be executed for your extension.
Now, make the label update very 3 seconds. You will implement a timer that updates the label, and then clear the time when your extension is removed.
In the /src/my-app/extensions/first-extension.js
file:
console.log
statements for debugging the update of attributes and the cleanup logic.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((extensionAPI) => { let clicked = 0; let changed = 0; // update the label every 3 seconds const interval = setInterval(() => { console.log('interval executed...'); changed += 1; extensionAPI.updateAttributes({ label: `This label has changed ${changed} times...`, }); }, 3000); // clear interval before destroying the extension extensionAPI.onCleanup(() => { console.log('interval cleared'); clearInterval(interval); }); return { label: `Go ahead, click me!`, iconBefore: 'app-access', onAction: () => { clicked += 1; extensionAPI.updateAttributes({ label: `Clicked: ${clicked} times. Amazing!`, }); }, }; });
After refreshing your pull request, the extension’s label should be changing every 3 seconds. To check if the timer is being cleared, open the diff
tab. The timer should stop logging interval executed…
.
To see what happens if you don’t have the cleanup logic, delete the onCleanup
execution, and repeat the test. Now, even if you navigate to another page and your extension isn’t visible, the interval should still be running.
So far, you've been using your local state for the extension, but CSE also lets products share part of their state with extensions. This state is called context
, and it needs to be provided as the second argument in your extension factory.
In the /src/my-app/extensions/first-extension.js
file:
context
.console.log
to preview what context is shared with your extension by the Bitbucket extension point.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((extensionAPI, context) => { console.log({ context }); return { label: `Go ahead, click me!`, iconBefore: 'app-access', onAction: () => { alert('clicked.'); }, }; });
After refreshing your pull request, you should see an object in the console that contains all the information being shared with your extension.
The value of context
can be different depending on the page and the extension point you're creating your extension for.
You can explore the extension point information to learn about the context it provides.
By finishing this section, you have learned:
Next, you will learn how to create an extension with custom HTML content
Rate this page: