Creating your own UI extension points in Umbraco v14 - Part 3: Customizable APIs
Matt Brailsford
Posted on April 9, 2024
In the last post in this series, we looked at componentizing the UI for our "quick action" elements.
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;
}
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>;
}
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;
}
}
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>`
}
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}`);
}
}
// ./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.
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 👋
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
April 18, 2024
April 11, 2024
April 9, 2024