DEV Community

Matt Brailsford
Matt Brailsford

Posted on • Edited on • Originally published at mattbrailsford.dev

Creating your own UI extension points in Umbraco v14 - Part 4: Interchangeable Elements

In the last post we looked at how we can swap out our "quick action" button behavior whenever a button is clicked.

Quick Action Buttons

In this post we'll take a look at how we could allow someone to completely replace the button element with some other user control.

Element Extensions

To allow our actions to support a interchangeable elements we first need to update our manifest definition again.

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

export interface QuickActionElement extends UmbControllerHostElement {
    manifest: UcManifestEntityQuickAction;
    api?: QuickActionApi;
}
Enter fullscreen mode Exit fullscreen mode

Here again we swap out the base interface and now extend from ManifestElementAndApi<>. We also define a QuickActionElement interface which lays out the required properties that any element used as a quick action must support. This interface, along with our previously defined API interface, are passed to the ManifestElementAndApi<> interface as generic arguments.

If we take a look at the ManifestElementAndApi<> interface we can see that our manifest now supports a couple of extra properties.

export interface  ManifestElementAndApi<ElementType, ApiType> extends ManifestBase {
    element?: ElementLoaderProperty<ElementType>;
    elementName?: string;
    ...
}
Enter fullscreen mode Exit fullscreen mode

Default Element Updates

With our element interface defined, we can go back to our default button implementation and ensure this now implements our required interface.

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

    @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

Overriding the Default Element

With everything configured developers can now override the default button element by firstly creating their own component that implements our interface.

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

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

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

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

    render() {
        return html`RENDER YOUR COMPONENT HERE`
    }
}

export default MyQuickActionElement;

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

Then updating the manifest to use this component instead.

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

What's next?

In this post we've looked out how to allow developers to override the default element of an extension.

In the next post we'll take a look at how we can reduce some repetition in our manifest definitions by creating reusable 'kinds'.

Until then 👋

Top comments (0)