Creating your own UI extension points in Umbraco v14 - Part 3: Customizable APIs
Tags: webdev, javascript, umbraco, extensions
Published: 2024-04-09 08:00:00

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

![Quick Action Buttons](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6usoxczmfxg7tnf10m96.png)

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.

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

export interface QuickActionApi extends UmbApi {
    manifest: ManifestQuickAction;
    execute(): void;
}
```

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.

```javascript
export interface  ManifestApi<ApiType> extends ManifestBase {
    api?: ApiLoaderProperty<ApiType>;
}
```

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:

```javascript
@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;
    }
}
```

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:

```javascript
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>`
}
```

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.

```javascript
// ./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}`);
    }

}
```
```javascript
// ./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}`);
    }

}

```

## Manifest Updates

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

```javascript
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"
        }
    }
]
```

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 👋