Creating your own UI extension points in Umbraco v14 - Part 3: Customizable APIs

mattbrailsford

Matt Brailsford

Posted on April 9, 2024

Creating your own UI extension points in Umbraco v14 - Part 3: Customizable APIs

In the last post in this series, we looked at componentizing the UI for our "quick action" elements.

Quick Action Buttons

In this post we'll take a look at how we can allow developers to override the default behavior of our buttons when they are clicked.

API Extensions

In Umbravo v14, the mechanism of replacing the behavior of an extension is via a custom API implementation.

To allow our actions to support a customizable API we first need to update our manifest definition.

export interface ManifestQuickAction extends ManifestApi<QuickActionApi> {
    type: 'quickAction';
    meta: MetaQuickAction;
}

export interface QuickActionApi extends UmbApi {
    manifest: ManifestQuickAction;
    execute(): void;
}
Enter fullscreen mode Exit fullscreen mode

Instead of extending from ManifestBase like we did in the last post, we now extend from ManifestApi<>. We also define a QuickActionApi interface which holds a reference to our manifest and defines the signature of a supported execute behavior. This interface is passed to the ManifestApi<> interface as a generic argument so that it knows which API type to expect.

If we take a look at the ManifestApi<> interface we can see that our manifest now supports a new property.

export interface  ManifestApi<ApiType> extends ManifestBase {
    api?: ApiLoaderProperty<ApiType>;
}
Enter fullscreen mode Exit fullscreen mode

We'll take a look at how to use it shortly, but we first need to update our button component to support custom APIs.

UI Updates

We'll update our component as follows:

@customElement("quick-action")
export class QuickActionElement extends UmbElementMixin(LitElement) {

    @property({ type: Object, attribute: false })
    manifest!: ManifestQuickAction;

    @property({ type: Object, attribute: false })
    api?: QuickActionApi;

    #onClick(e: Event) {
        this.api?.execute();
    }

    render() {
        return html`<uui-button 
                look=${this.manifest.meta.look ?? 'secondary'}
                @click=${this.#onClick}>
                ${this.manifest.meta.label}
           </uui-button>`
    }
}

export default QuickActionElement;

declare global {
    interface HTMLElementTagNameMap {
        "quick-action": QuickActionElement;
    }
}
Enter fullscreen mode Exit fullscreen mode

Here we new have a new optional api property of type QuickActionApi. We've also also moved our click handler to a dedicated private handler function and we have that call the APIs execute() method in replace of the console.log statement.

Next, we need to update our workspaces render function as follows:

render() {
   return html`<uui-box headline="Actions">
       <umb-extension-with-api-slot
           type="quickAction"
           default-element="quick-action-button">
       </umb-extension-with-api-slot>
   </uui-box>`
}
Enter fullscreen mode Exit fullscreen mode

Here we have simply switched from using a umb-extension-slot helper component, to use a umb-extension-with-api-slot helper component instead. This new component does everything the previous other does, but also instantiates and injects API instances into our button components.

API Implementations

Now that our UI is capable of executing our custom API's, lets define some API implementations for our two buttons.

// ./components/send-email.quick-action.api.ts
export default class SendEmailQuickActionApi extends UmbControllerBase implements QuickActionApi {

    manifest!: UcManifestEntityQuickAction;

    execute() {
        console.log(`You clicked ${this.manifest.meta.label}`);
    }

}
Enter fullscreen mode Exit fullscreen mode
// ./components/change-status.quick-action.api.ts
export default class ChangeStatusQuickActionApi extends UmbControllerBase implements QuickActionApi {

    manifest!: UcManifestEntityQuickAction;

    execute() {
        console.log(`Now you clicked ${this.manifest.meta.label}`);
    }

}

Enter fullscreen mode Exit fullscreen mode

Manifest Updates

Finally, we can now update our manifest to reference our API implementations.

export const quickActionManifests: ManifestQuickAction[] = [
    {
        type: 'quickAction',
        alias: 'Mb.QuickAction.SendEmail',
        name: 'Send Email Quick Action',
        weight: 200,
        api: SendEmailQuickActionApi,
        meta: {
            label: "Send Email",
            look: "primary"
        }
    },
    {
        type: 'quickAction',
        alias: 'Mb.QuickAction.ChangeStatus',
        name: 'Change Status Quick Action',
        weight: 100,
        api: ChangeStatusQuickActionApi,
        meta: {
            label: "Change Status"
        }
    }
]
Enter fullscreen mode Exit fullscreen mode

And now when we click each of our quick actions, the defined API's will be instantiated and called allowing complete customization of the buttons behavior.

What's next?

In this post we've outlined how to allow customizable APIs for extensions. But what if you also wanted to allow swapping out the button component entirely?

In our next post we'll take a look how we can go about just that.

Until then 👋

💖 💪 🙅 🚩
mattbrailsford
Matt Brailsford

Posted on April 9, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related