This is a guide for the deprecated CSE version v1.x.
Visit the updated reference guide for the latest API.
This is a guide for product developers or extension point owners.
First, make sure to install and provide Client-side Extensions in your Product as described in the Installation Guide for v1.
Next, add @atlassian/clientside-extensions-components
to your project.
These are a set of React components and hooks that will help you use the APIs when your code is written in React. As of this point no other framework is supported.
1 2yarn add -D @atlassian/clientside-extensions-components # or npm install --save-dev @atlassian/clientside-extensions-components
useExtensions
In order to provide a new extension point, import the useExtensions
hook from the @atlassian/clientside-extensions-components
package and use it as follows:
1 2import React from 'react'; import { useExtensions } from '@atlassian/clientside-extensions-components'; // 1. Define a JSON schema that describes the extension attributes. // To read more about JSON schema format check the official docs https://json-schema.org/ const extensionPointSchema = { type: 'object', properties: { type: { enum: ['button'], description: 'This extension point supports a button type', }, label: { type: 'string', description: 'This button label', }, onAction: { typeof: 'function', description: 'Callback that will be run when user clicks the button', }, }, }; export default function MyComponent() { // 2. Use the `useExtensions` hook to retreive the extensions. The first param passed to the hook is the name of the extension point. // This is what the extension developers will use to create extensions so think carefully how to name it. // The second is the context value. We will talk about it later. // Finally, the third param is the options object where you provide the JSON schema for the extension attributes. const extensions = useExtensions('example.extension.point.name', null, { schema: extensionPointSchema }); return ( </> {extensions.map(extensionDescriptor => { // 3. Read the attributes from the descriptor and render the button const { key, attributes } = extensionDescriptor; return <button key={key} onClick={attributes.onAction}>{attributes.label}</button> // ...render them as you need })} </> ); }
What you receive is a list of descriptors that then you can render as you want. The shape of such descriptors is:
1 2interface ExtensionDescriptor { // Unique extension key key: string; // Location, the name of the extension point location: string; // A number that can be used to decide about the order of extensions weight: number; // A map of all the attributes provided by the extension author attributes: { type: string; onAction?: () => void; [key: string]: unknown; }; }
You probably are most interested in the attributes
property, which will be populated with the attributes provided by the extensions.
Descriptors are delivered already sorted by weight
(from highest to lowest), but you can always sort them differently before rendering.
useExtensionsLoadingState
You can use this hook in order to indicate your users that extensions, and their attributes are loading.
1 2import React from 'react'; import { useExtensions, useExtensionsLoadingState } from '@atlassian/clientside-extensions-components'; import schema from './schema.json'; export default function MyComponent() { const extensions = useExtensions('example.extension.point.name', null, { schema }); const loading = useExtensionsLoadingState('example.extension.point.name', null, { schema }); return ( <> {loading ? 'loading...' : extensions.map(ext => ( // ...render them as you need ))} </> ) }
useExtensionsUnsupported
There might be cases where you want to support legacy WebItems
that are created using only the XML definitions inside atlassian-plugin.xml
plugin descriptor file.
You can make use of useExtensionsUnsupported
hook to gather all those extensions, and then show them as you want.
1 2import React from 'react'; import { useExtensionsUnsupported } from '@atlassian/clientside-extensions-components'; import schema from './schema.json'; export default function MyComponent() { const extensions = useExtensionsUnsupported('example.legacy-web-item-location-name', null, { schema }); return ( <> {extensions.map((ext) => ( <a href="ext.url">{ext.label}</a> ))} </> ); }
useExtensionsAll
You can use useExtensionsAll
in case you need all the results from useExtensions
, useExtensionsLoadingState
and useExtensionsLoadingState
and prefer to get the results with a single hook.
1 2import React from 'react'; import { useExtensionsAll } from '@atlassian/clientside-extensions-components'; import schema from './schema.json'; export default function MyComponent() { const [extensions, legacyExtensions, loading] = useExtensionsAll('example.extension.point.name', null, { schema }); const allExtensions = [...extensions, ...legacyExtensions].sort((a, b) => a.weight - b.weight); return ( <> {loading ? 'loading...' : allExtensions.map(ext => ( // ...render them as you need ))} </> ); }
You can share some context data with the extensions. Think about the context as the payload of the extension. This is especially useful when you want to provide extension authors with additional data that developers can use to build the extension.
1 2import React from 'react'; import { useExtensions } from '@atlassian/clientside-extensions-components'; import extensionSchema from './schema.json'; // A JSON schema that describes the extension attributes // A JSON schema that describes the extension context const extensionContextSchema = { type: 'object', properties: { issueKey: { type: string, description: 'A Jira issue key', } } }; export default function IssueView({ issueKey }) { const context = { issueKey: issueKey, }; const [extensions, isLoading] = useExtensions('example.extension.point.name', context, { schema: extensionSchema, contextSchema: extensionContextSchema, }); return ( <> {extensions.map(descriptor => ( // ...render them as you need ))} </> ) }
ExtensionPointInfo
componentIn order to inform extension developers which types of extensions and set of attributes are supported in your locations, you need to define a Schema object.
A Schema is JSON Schema that might looks as this example:
schema.json
1 2{ "type": "object", "properties": { "type": { "type": "string", "description": "Supported extension types", "enum": ["modal", "link", "button", "panel"] }, "onAction": { "description": "Callback triggered on user interaction with the extension. Signature depends on extension type.", "type": "function" }, "glyph": { "type": "string", "description": "Atlaskit Glyph name to render as an icon", "enum": ["cross", "check"] }, "tooltip": { "type": "string", "description": "Tooltip content" }, "hidden": { "type": "boolean", "description": "Hidden flag to hide the extension" }, "disabled": { "type": "boolean", "description": "Disabled flag to disable the extension" }, "loading": { "type": "boolean", "description": "Renders the extension in a loading state (if supported)" } }, "required": ["type", "glyph", "tooltip"] }
You can describe as many attributes as you need, but don't forget to specify the types you support in your extension point in order to avoid unhandled extensions.
Don't set onAction
as required if you're accepting Link
extensions, since they don't use the onAction
API.
ExtensionPointInfo
You can add this component to share with extension developers the schema of your Locations, and also highlight the locations in the current screen.
The information will only be available if the product has enabled their display in development mode.
1 2import React from 'react'; import { useExtensions, ExtensionPointInfo } from '@atlassian/clientside-extensions-components'; import schema from './schema.json'; import contextSchema from './context-schema.json'; export default function MyComponent({ context }) { // Third param of `useExtensions` is a configuration object. // Add the schema as a property of it. const [extensions, isLoading] = useExtensions('example.extension.point.name', context, { schema, contextSchema }); return ( <> <h4> Product Location <ExtensionPointInfo name="example.extension.point.name" schemas={{ schema, contextSchema }} /> </h4> {extensions.map(ext => ( // ...render them as you need ))} </> ) }
Render the ExtensionPointInfo
in a place that's visible even in edge cases, like when the location is inside a
dropdown (place it in the dropdown trigger), or part of a Grid (place it in the header).
Handlers continue to work the same on the recent version of CSE. Refer to the extension handlers guide to learn how to use them.
Refer to the extension handlers guide.
Rate this page: