Creating your own UI extension points in Umbraco v14 - Part 6: Filters & Conditions
Tags: webdev, javascript, umbraco, extensions
Published: 2024-04-18 08:00:00

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

![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 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.

```javascript
export interface MetaQuickAction  {
    entityType: string;
    label: string;
    look?: 'primary' | 'secondary';
}
```

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

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

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

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

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:

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

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.

```javascript
export interface ManifestWithDynamicConditions {
    conditions?: Array<ConditionTypes>;
}
```

### 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:

```javascript
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',
            },
        ]
    },
    ...
]
```

> **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.

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

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

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

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.

```javascript
export const onInit: UmbEntryPointOnInit = (host, extensionRegistry) => {
    extensionRegistry.registerMany(conditionManifests);
};
```

### Configuration Update

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

```javascript
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',
            },
        ]
    },
    ...
]
```

## 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 👋