So far in this series we have looked at creating a flexible and extendable "quick actions" feature.
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';
}
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"
}
},
...
]
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>`
}
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;
}
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>;
}
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',
},
]
},
...
]
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,
}
];
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;
};
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);
};
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',
},
]
},
...
]
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)