DEV Community

Matt Brailsford
Matt Brailsford

Posted on

Creating your own UI extension points in Umbraco v14 - Part 6: Filters & Conditions

So far in this series we have looked at creating a flexible and extendable "quick actions" feature.

Quick Action Buttons

In this post we'll take a look at how we can control when our quick actions will display by using filtering and conditions.

It's very likely that we are going to want to display quick actions in multiple places of our application, yet not all actions are going to suitable for all areas. This is whether filters and conditions come in to play in order to control when a quick action actually displays.

Filtering

We will use filtering to control displaying items based on more static attributes, such as a property we define in our manifest.

If we assume that actions in our quick actions list will be entity specific, the simplest filtering we could perform would be to only show actions that are suitable for a given entity type.

To achieve this, lets update our MetaQuickAction to accept a new entityType property.

export interface MetaQuickAction  {
    entityType: string;
    label: string;
    look?: 'primary' | 'secondary';
}
Enter fullscreen mode Exit fullscreen mode

Next, we'll update our manifest definitions to set which entity type the action is suitable for.

export const quickActionManifests: ManifestQuickAction[] = [
    {
        type: 'quickAction',
        kind: 'primary'
        alias: 'Mb.QuickAction.SendEmail',
        name: 'Send Email Quick Action',
        weight: 200,
        meta: {
            entityType: "uc:order",
            label: "Send Email"
        }
    },
    ...
]
Enter fullscreen mode Exit fullscreen mode

And now in our entities workspace elements render function, we can update the umb-extension-with-api-slot to pass in a filter.

render() {
   return html`<uui-box headline="Actions">
       <umb-extension-with-api-slot
           type="quickAction"
           filter=${(ext) => ext.meta.entityType === 'uc:order'}
           default-element="quick-action-button">
       </umb-extension-with-api-slot>
   </uui-box>`
}
Enter fullscreen mode Exit fullscreen mode

A filter is a defined inline as a lambda expression that takes a manifest and performs a check against it. If the lambda returns true, the manifest will be included, otherwise it will be excluded from the output.

With this, in each entities workspace we can change the entity type to that of the given workspace and be sure we'll only display actions that are suitable for our given entity.

Conditions

Where filters allow us to control the display of our actions by manifest properties, conditions allow us to control the display based on more dynamic conditions that may only be known at runtime.

We firstly enable conditions support by updating our manifest interface as follows:

export interface ManifestQuickAction extends ManifestElementAndApi<QuickActionElement, QuickActionApi>,
    ManifestWithDynamicConditions {
    type: 'quickAction';
    meta: MetaQuickAction;
}
Enter fullscreen mode Exit fullscreen mode

Here we have our ManifestQuickAction extend from both the previous ManifestElementAndApi<> interface and a new ManifestWithDynamicConditions interface.

If we take a look at the ManifestWithDynamicConditions interface we can see that our manifest now supports a conditions property.

export interface ManifestWithDynamicConditions {
    conditions?: Array<ConditionTypes>;
}
Enter fullscreen mode Exit fullscreen mode

Configuration

Configuring conditions is done against the manifest definitions.

To limit a quick action to only display if a user belongs to a given user group, we can configure our manifest as follows:

export const quickActionManifests: ManifestQuickAction[] = [
    {
        type: 'quickAction',
        kind: 'primary'
        alias: 'Mb.QuickAction.SendEmail',
        name: 'Send Email Quick Action',
        weight: 200,
        meta: {
            entityType: "uc:order",
            label: "Send Email"
        },
        conditions: [
            {
                alias: 'Umb.Condition.UserGroup',
                match: 'admin',
            },
        ]
    },
    ...
]
Enter fullscreen mode Exit fullscreen mode

NB The Umb.Condition.UserGroup condition doesn't actually exist in core yet, but I believe it will so have demonstrated it here, but if you need this functionality now, you may need to implement your own in the meantime (see below).

Custom Conditions

We aren't just limited to the out of the box conditions either. If needed, we can actually implement our own.

As you might expect, conditions are yet another manifest type.

export const conditionManifests: Array<ManifestCondition> = [
    {
        type: 'condition',
        name: 'My Condition',
        alias: 'Mb.Condition.MyCondition',
        api: MyCondition,
    }
];
Enter fullscreen mode Exit fullscreen mode

Our custom condition in turn references a custom condition API implementation MyCondition.

export class MyCondition extends UmbConditionBase<MyConditionConfig> implements UmbExtensionCondition
{
    constructor(host: UmbControllerHost, args: UmbConditionControllerArguments<MyConditionConfig>) {
        super(host, args);

        // Do your logic here and set `this.permitted`
        // to a boolean value
        // async behavior is supported
        this.permitted = args.config.match == 'ALLOWED';
    }
}

export type MyConditionConfig = UmbConditionConfigBase<'Mb.Condition.MyCondition'> & {
    match: string;
};
Enter fullscreen mode Exit fullscreen mode

The API implementation can define a config object for any parameters it might need, in our case we require a single match parameter. Then, within the conditions constructor it should perform it's checks and set it's permitted field to the result of that check.

As with any manifest definitions, we must register them with the extensions registry.

export const onInit: UmbEntryPointOnInit = (host, extensionRegistry) => {
    extensionRegistry.registerMany(conditionManifests);
};
Enter fullscreen mode Exit fullscreen mode

Configuration Update

We can now update our manifest definition to use our custom condition.

export const quickActionManifests: ManifestQuickAction[] = [
    {
        type: 'quickAction',
        kind: 'primary'
        alias: 'Mb.QuickAction.SendEmail',
        name: 'Send Email Quick Action',
        weight: 200,
        meta: {
            entityType: "uc:order",
            label: "Send Email"
        },
        conditions: [
            {
                alias: 'Mb.Condition.MyCondition',
                match: 'ALLOWED',
            },
        ]
    },
    ...
]
Enter fullscreen mode Exit fullscreen mode

Conclusion

This is the last post in a series about creating your own UI extension points in Umbraco v14. We've covered a whole bunch of things from basic extension definitions, to swappable APIs and interchangeable elements along with reusable kinds and condition flows.

Hopefully you've found this series useful.

Until next time 👋

Top comments (0)