Creating your own UI extension points in Umbraco v14 - Part 1: The Basics
Matt Brailsford
Posted on April 2, 2024
In this mini series I'll take a look at a progressively more advanced example of creating your own UI extensions in Umbraco v14.
I'll loosely base this on a feature in Umbraco Commerce where we display a series of "Quick Action" buttons in a few of the editor views to provide instant access to important features.
We provide a number of quick actions out of the box, but we also want to provide a means fore developers to add their own based on their customers needs.
NB As you might expect, this is considered a fairly advanced topic for Umbraco v14 and so in the name of brevity, I assume you know a lot of fundamentals of developing for v14. If you are just getting started, you might want to go do some basic learning first before tackling this subject.
In this first post we'll take a look at the most basic implementation of a UI extension point for registering our buttons.
Definition
The foundation of extending the UI in Umbraco v14 lies in defining custom manifests.
Let's start by defining a basic extension manifest interface:
export interface ManifestQuickAction extends ManifestBase {
type: 'quickAction';
meta: MetaQuickAction;
}
export interface MetaQuickAction {
label: string;
look?: 'primary' | 'secondary';
}
A manifest consists of two parts. The manifest definition and a meta data object for defining custom properties we want to capture.
In this example we define a ManifestQuickAction
manifest and give it a type
of "quickAction". This will be how we identify our quick actions and will be used later to look up all manifests with this type.
We also define a meta
property of type MetaQuickAction
which at this point simply holds a label
and optional look
attribute which will control the text and style of our button.
In addition to the properties we've defined, we also get a number of standard manifest properties from the ManifestBase
interface.
export interface ManifestBase {
type: string;
alias: string;
kind?: unknown;
name: string;
weight?: number;
}
These are standard for all manifests and mostly provide a means of identifying an individual manifest definition and controlling it's position.
Implementation
With our manifest interface defined, we can now define our manifest implementations.
export const quickActionManifests: ManifestQuickAction[] = [
{
type: 'quickAction',
alias: 'Mb.QuickAction.SendEmail',
name: 'Send Email Quick Action',
weight: 200,
meta: {
label: "Send Email",
look: "primary"
}
},
{
type: 'quickAction',
alias: 'Mb.QuickAction.ChangeStatus',
name: 'Change Status Quick Action',
weight: 100,
meta: {
label: "Change Status"
}
}
]
Registration
With our manifests defined we then register them with the extension registry from within our onInit
entry point method.
export const onInit: UmbEntryPointOnInit = (host, extensionRegistry) => {
extensionRegistry.registerMany(quickActionManifests);
};
Rendering
With all our manifests now registered, the next step is to render our buttons for each manifest.
Within the constructor of our workspace element we'll first lookup our manifest instances by querying the extensions registry.
constructor(host: UmbControllerHost) {
super(host);
const quickActionsObservable = umbExtensionsRegistry.byType<string, ManifestQuickAction>('quickAction');
}
As with most things in Umbraco v14, what we get back from our query is an observable and so we will want to observe the collection and store it's value in a local state variable.
@state()
_quickActions: ManifestQuickAction[] = [];
constructor(host: UmbControllerHost) {
super(host);
const quickActionsObservable = umbExtensionsRegistry.byType<string, ManifestQuickAction>('quickAction');
this.observe(quickActionsObservable, (manifests) => {
this._quickActions = manifests
})
}
With access to our actions, we can then output our buttons in our workspace components render method. For now we'll just output the actions label to the console when the button is clicked.
render() {
return html`<uui-box headline="Actions">
${repeat(this._quickActions,
(qa) => qa.alias,
(qa) => html`<uui-button
look=${this.manifest.meta.look ?? 'secondary'}
@click=${() => console.log(qa.meta.label)}>
${qa.meta.label}
</uui-button>`
)}
</uui-box>`
}
What's next?
In this post I've shared the most basic example of creating a custom UI extension. If in your use case all you need is to allow developers to provide some level of configuration that you will process and handle in a predefined way, then this should cover all you need.
If as in our example however, your configuration has a 1-to-1 relation to a rendered element (ie, our quick action button) then we can actually go a little bit further, as whilst we have allowed developers to define their own quick action buttons, the implementation of what a quick action is and does is really rather restrictive.
In the next few posts in this series I'll take a look at how we can make this even more flexible by allowing developers to change the buttons behavior when clicked and also how they could swap out the button component entirely.
Until next time 👋
Posted on April 2, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.