This is a continuation of the work done in 1. Creating an extension point.
In this section you will learn how to:
You'll continue working on the tutorial page: http://localhost:7990/bitbucket/plugins/servlet/extension-points
On the previous guide, you defined a schema for an extension point and imported the helpers using the CSE schema-loader to fetch and validate the extensions.
You can call the useExtensions
hook helper to retrieve a list of extension descriptors. An
extension descriptor is nothing more than an object that contains the attributes of an
extension like:
1 2interface ExtensionAttribute { key: string; location: string; attributes: { // the attributes of an extension }; }
After you get the extension, you can make use of their attributes as you see fit. For example, if you want to render link and button extensions, you can do so as follows:
On ./src/main/my-app/extensions/extension-points-tutorial/extension-points-page.jsx
, write:
1 2import React from 'react'; import { useExtensions } from './schema.cse.graphql'; const MyPage = () => { const extensions = useExtensions(); const renderExtension = (extension) => { const { key, attributes } = extension; switch (attributes.type) { case 'link': return ( <a href={attributes.url} key={key}> {attributes.label} </a> ); case 'button': return ( <button type="button" onClick={attributes.onAction} key={key}> {attributes.label} </button> ); default: return null; } }; return ( <PageContainer> <h2>extension.points.tutorial</h2> {extensions.map(renderExtension)} </PageContainer> ); }; /** page declaration for guides only **/
Go to http://localhost:7990/bitbucket/plugins/servlet/extension-points, and you should see a link and a button extension rendered on the screen.
Rendering the same type of extensions can become repetitive, and even time consuming for more complex examples. That's why CSE provides a set of components called handlers, that will take the attributes and render them, also matching the style of Atlassian products automatically for you.
You could rewrite the previous code to use Link and Button handlers as follows:
On ./src/main/my-app/extensions/extension-points-tutorial/extension-points-page.jsx
, write:
1 2import React from 'react'; import { LinkHandler, ButtonHandler } from '@atlassian/clientside-extensions-components'; import { useExtensions } from './schema.cse.graphql'; const MyPage = () => { const extensions = useExtensions(); const renderExtension = (extension) => { const { key, attributes } = extension; switch (attributes.type) { case 'link': return ( <LinkHandler href={attributes.url} key={key}> {attributes.label} </LinkHandler> ); case 'button': return ( <ButtonHandler onAction={attributes.onAction} key={key}> {attributes.label} </ButtonHandler> ); default: return null; } }; return ( <PageContainer> <h2>extension.points.tutorial</h2> {extensions.map(renderExtension)} </PageContainer> ); }; /** page declaration for guides only **/
The handler code for links and buttons is similar, except that the button is rendered with the same styles as Atlassian products without extra effort.
To learn more about handlers, refer to the handlers reference guide.
Modal extensions allow developers to render a button that displays a modal with custom content when clicked.
The API for Modal extensions is quite complex to set up due to all its different options. In this scenario, you’ll need to use handlers.
First, modify your schema to accept extensions of type ModalExtension
on
/src/main/my-app/extensions/extension-points-tutorial/schema.cse.graphql
:
1 2""" --- extensionPoint: extension.points.tutorial --- """ type Schema { type: SupportedExtensions! label: String url: String onAction: Function } union SupportedExtensions = LinkExtension | ButtonExtension | ModalExtension
Then, modify your extension point to render modals with the modal handler on
./src/main/my-app/extensions/extension-points-tutorial/extension-points-page.jsx
:
1 2import React from 'react'; import { ModalWithActionHandler } from '@atlassian/clientside-extensions-components'; import { useExtensions } from './schema.cse.graphql'; const MyPage = () => { const extensions = useExtensions(); const renderExtension = (extension) => { const { key, attributes } = extension; switch (attributes.type) { case 'modal': return ( <ModalWithActionHandler render={attributes.onAction} key={key}> {attributes.label} </ModalWithActionHandler> ); /** other cases **/ default: return null; } }; return ( <PageContainer> <h2>extension.points.tutorial</h2> {extensions.map(renderExtension)} </PageContainer> ); }; /** page declaration for guides only **/
The provided modal handler will create a Modal API object and pass it to the extensions, and will also create an implementation of this API using an Atlaskit modal dialog.
If you refresh the page, you should see a button that opens a modal when clicked.
Panel extensions allow developers to render any HTML content inside a given container.
Even though the API for panels is simpler than modals, it can still be challenging to handle the rendering and clean-up cycles efficiently every time the extension point re-renders.
The panel handle takes care of this problem by creating an empty container and handling the rendering and cleanup cycles for you.
First, modify your schema to accept extensions of type PanelExtension
on
/src/main/my-app/extensions/extension-points-tutorial/schema.cse.graphql
:
1 2""" --- extensionPoint: extension.points.tutorial --- """ type Schema { type: SupportedExtensions! label: String url: String onAction: Function } union SupportedExtensions = LinkExtension | ButtonExtension | ModalExtension | PanelExtension
Then, modify your extension point to render panels with the panel handler on
./src/main/my-app/extensions/extension-points-tutorial/extension-points-page.jsx
:
1 2import React from 'react'; import { PanelHandler } from '@atlassian/clientside-extensions-components'; import { useExtensions } from './schema.cse.graphql'; const MyPage = () => { const extensions = useExtensions(); const renderExtension = (extension) => { const { key, attributes } = extension; switch (attributes.type) { /** other cases **/ case 'panel': return <PanelHandler render={attributes.onAction} key={key} />; default: return null; } }; return ( <PageContainer> <h2>extension.points.tutorial</h2> {extensions.map(renderExtension)} </PageContainer> ); }; /** page declaration for guides only **/
You should now see an extension rendered with a title "Look!" and a paragraph that counts the times the panel has been re-rendered.
So far, you've learned:
Next, you're going to learn how to provide context to extensions.
Rate this page: