Styling the Textarea Property Editor in Optimizely CMS 12

on March 15, 2022 in Optimizely, Optimizely CMS, Episerver, Development

What first started out as a "nice to have" feature, has now turned into a bigger experiment with EditorDescriptor. The original idea was pretty simple: "Can the font be changed in the Optimizely CMS UI for a textarea (string) property editor, maybe to something like a monospaced font where editors can provide additional styles or scripts?"

Here's a quick write-up about what I've learned when creating and modifying client side property editors.

The Original Goal

As I said, the goal of this is to find a way to add additional CSS styles to a textarea property editor in the CMS UI. This should be pretty trivial, right? It turns out that most examples which show how to create your own client-side editor involve creating a custom Dojo/Dijit widget.

I'll be honest... I don't like Dojo (shocking, I know), and I really don't want to create a custom client-side editor to make this work, especially since the "feature" is so minimal. The plan is to write the least amount of code for this to function.

And, not to mention, it should be developer-friendly to implement and use.

One Option (Though A Bit Unfriendly)

Why not just add our own custom CSS to the whole edit UI? That's pretty simple...

First, we'll need a CSS file with our custom style. We're going to be a bit more specific with our style declarations, so we only target the actual textarea property editors:

.Sleek textarea.dijitTextArea {
    font-family: Consolas, Lucida Console, monospace;
}

We're going to name this file epi-cms-editor.css. Since we are using Optimizely CMS 12, the CSS file will need to live in the wwwroot/ClientResources/Styles folder. If you are still using Episerver CMS 11, the file will just go in the ClientResources/Styles folder.

In order to register that CSS file, we'll need to add/update the module.config file:

<?xml version="1.0" encoding="utf-8"?>
<module>
    <clientResources>
        <add name="epi-cms.widgets.base" path="Styles/epi-cms-editor.css" resourceType="Style"/>
    </clientResources>
</module>

This is nice, but now all textarea property editors will have this same style. What if I wanted to apply a custom style to just one textarea property editor?

TextareaStyles Attribute and EditorDescriptor

To add custom styling to just one textarea property editor, we're going to extend the TextareaEditorDescriptor, which is the class that Optimizely uses to tell the system which Dojo/Dijit widget to use, and how the widget should be configured and rendered to the editor.

But first, let's create an attribute called TextareaStyles to decorate our properties with the additional CSS styles:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class TextareaStylesAttribute : Attribute
{
    public TextareaStylesAttribute(string styles)
    {
        Styles = styles;
    }

    public string Styles { get; private set; }
}

Then, we'll create our custom TextareaStylesEditorDescriptor:

[EditorDescriptorRegistration(EditorDescriptorBehavior = EditorDescriptorBehavior.OverrideDefault, TargetType = typeof(string), UIHint = UIHint.Textarea)]
public class TextareaStylesEditorDescriptor : EPiServer.Cms.Shell.UI.ObjectEditing.EditorDescriptors.TextareaEditorDescriptor
{
    public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable<Attribute> attributes)
    {
        base.ModifyMetadata(metadata, attributes);

        var attribute = attributes.OfType<TextareaStylesAttribute>().FirstOrDefault();

        if (attribute == null)
            return;

        var previousStyles = metadata.EditorConfiguration.ContainsKey("style") ? metadata.EditorConfiguration["style"].ToString() : string.Empty;
        var attributeStyles = attribute.Styles ?? string.Empty;

        metadata.EditorConfiguration["style"] = string.Concat(previousStyles, attributeStyles);
    }
}

In this custom EditorDescriptor, we are essentially replacing the out-of-the-box TextareaEditorDescriptor. You'll see in the EditorDescriptorRegistration attribute that we are setting the EditorDescriptorBehavior parameter to OverrideDefault and the UIHint parameter to the built-in UIHint.Textarea. This means all properties that are decorated with that UIHint will use this custom EditorDescriptor, regardless whether it's also decorated with the TextareaStyles attribute.

In the ModifyMetadata method, we're letting the original descriptor set everything up first, then we are adding our custom CSS styles to the EditorConfiguration, as long as that attribute is present.

To use this, we just need to decorate our content type properties with the TextareaStyles attribute:

[UIHint(UIHint.Textarea)]
[TextareaStyles("font-family: Consolas, Lucida Console, monospace;")]
public virtual string AdditionalStyles { get; set; }

That looks pretty clean, right?

What About Other Property Editors?

If you're thinking about repeating this same functionality for other property editors, there is one caveat with this approach: Not all out-of-the-box editor descriptors have a "style" key/value in the EditorConfiguration. In fact, the TextareaEditorDescriptor is the only one I've found (of the basic editor descriptors) where this is possible.

So, if you want to add custom styles to other property editors, you'll likely need to create your own Dojo/Dijit widget.