Configuring Rich Text Blocks for Your Wagtail Site

Introduction

Rich text blocks are the most fundamental building block in any Wagtail site. It's important to get the structure right before building out the site, changing it later is going to require re-entering all of your rich text content. In this blog, I'll go through making some changes to Wagtail's built-in Draftail rich text editor:

  • By default, the toolbar is not sticky, which can be an inconvenience on long blocks - a simple CSS trick can be used to fix this.
  • You can make your code a lot tidier and standardised by defining the feature set as editors, using the little-known WAGTAILADMIN_RICH_TEXT_EDITORS setting.
  • Some useful features are missing from the built-in options. You can add these and your own custom features by extending the WSIWYG editor with your own additional custom features which I will go through in more detail in the following Extending the Draftail Editor series.

Set a Floating Toolbar on the Draftail Editor

Not to be confused with the 'floating' (i.e. hidden) toolbar introduced in Wagtail 4.0, this refers to ensuring the toolbar is always visible as the Draftail editor scrolls off the top of the screen. It's a pain to have to scroll up and down to access a formatting button on long text blocks.

Add the following class tweak to your admin site's CSS to make the toolbar float at the top of the screen until the rich text area scrolls out of view.

Copy
.Draftail-Toolbar {
  /* float toolbar so always visible */
  position: sticky !important;
  top: calc(0.2rem + 50px) !important;
  z-index: 2 !important;
}
@media screen and (max-width: 800px) {
  .Draftail-Toolbar {
    /* double top height when top menu wraps at 800px */
    top: calc(0.2rem + 100px) !important;
  }
}

Setting the Draftail Toolbar to Pinned as Default

Introduction of the 'hidden' toolbar

Wagtail 4.0 & 4.1 were released with a minimalistic draftail interface - toolbar hidden until you highlight text or add a "/" to an empty line ... I find it unusable myself, as did everyone I did UAT with. This was reversed in 4.2. I highly recommend upgrading from either of those two versions.

From Wagtail 4.2 the sticky toolbar was reintroduced as an option. Draftail now comes with the ability to pin the toolbar inside the rich text editor once again (look for the pin icon in the top right of the floating toolbar fa-solid fa-thumbtack).

The setting is stored locally in the browser as wagtail:draftail-toolbar = sticky once pinned, and wagtail:draftail-toolbar = floating. To view the setting in your browser, with a page open for editing, open the dev tools panel and go to Storage > Local Storage > Website address.

Before being set the first time, there is no stored setting. We can use this as a test and set the toolbar to sticky (i.e. pinned) as the default by including the following in your admin JavaScript:

Copy
if (window.localStorage.getItem("wagtail:draftail-toolbar")==null) {
    window.localStorage.setItem("wagtail:draftail-toolbar", "sticky");
};

Improve Draftail Toolbar Grouping

The toolbar buttons are grouped together in sets with class Draftail-ToolbarGroup. The display settings are such that the group will wrap as one instead of the individual buttons leading to some crazy stacking. Add the following class tweak to your admin css to fix that:

Copy
.Draftail-ToolbarGroup, .tab-content--comments-enabled .Draftail-CommentControl {
  /* allow toolbar button groups to wrap */
  display: contents !important;
}

Draftail Features

RichTextFields & RichTextBlocks use a set of 'features' to determine what's available on the editor toolbar. By default, those are:

  • h1, h2, h3, h4, h5, h6 - heading elements
  • bold, italic - bold / italic text
  • ol, ul - ordered / unordered lists
  • hr - horizontal rules
  • link - page, external and email links
  • document-link - links to documents
  • image - embedded images
  • embed - embedded media

I take out h1 as (for SEO reasons) this should be reserved for the page title only.

Next, I take out image and embed. Embedding graphics and media into rich text can have unexpected behaviour on different layouts and is very difficult to achieve a satisfactory result. Maybe I'm being controversial here, but a text block is for text, it's a straightforward exercise to create blocks for media combined with text that will have predictable responsive results.

Also included, but not by default are:

  • code - inline code
  • superscript, subscript, strikethrough - text formatting
  • blockquote - blockquote

The editor features can be altered by declaring the parameter as an array of feature names when creating the class instance, for example:

Copy
content = RichTextBlock(features= ['bold', 'italic', 'link'])

Use 'editors' to Pre-define Draftail Feature Sets

Rather than declaring a list of features each time you call RichTextBlock or RichTextField, you can predefine sets of features as editors using the WAGTAILADMIN_RICH_TEXT_EDITORS setting which you define in your base.py. The feature set defined as default will determine which features are present when calling the block or field without any parameters (e.g. content = RichTextBlock()).

An example set of editors might be:

settings/base.py
Copy
WAGTAILADMIN_RICH_TEXT_EDITORS = {
    'default': {
        'WIDGET': 'wagtail.admin.rich_text.DraftailRichTextArea',
        'OPTIONS': {
            'features': ['h2', 'h3', 'h4', 'bold', 'italic', 'link', 'ol', 'ul', 'hr']
        }
    },
    'full': {
        'WIDGET': 'wagtail.admin.rich_text.DraftailRichTextArea',
        'OPTIONS': {
            'features': ['h2', 'h3', 'h4', 'h5', 'h6', 'bold', 'italic', 'ol', 'ul',
                         'link', 'hr', 'code', 'document-link', 'blockquote']
        }
    },
    'minimal': {
        'WIDGET': 'wagtail.admin.rich_text.DraftailRichTextArea',
        'OPTIONS': {
            'features': ['bold', 'italic', 'link']
        }
    },
}

Now, for example, you can call a RichTextBlock with minimal features with

Copy
content = RichTextBlock(editor='minimal')

This provides a much more concise way to call rich text areas and also means you keep your settings centrally and easily manageable, rather than needing to update code in multiple areas of your site.

Adding Features to the Draftail Editor

You can add features to the built-in draftail editor through the use of Wagtail hooks. Covering what hooks are is beyond this article. Suffice to say that they're bits of code that can be run in response to certain events being triggered.

In this case, we use a hook to register a new feature for the editor by creating a custom plugin.

There are three types of plugins that can be created:

  • Inline styles – To format a portion of a line, eg. bold, italic, monospace.
  • Blocks – To indicate the structure of the content, eg. blockquote, ol.
  • Entities – To enter additional data/metadata, eg. link (with a URL), image (with a file).

More information and examples can be found in the next blog post, on the Wagtail Documentation article Extending the Draftail Editor and on the Draftail documentation site. The documentation isn't great, so there's a fair amount of experimentation and discovery in dusty corners of StackOverflow to be done.

Conclusion

In this article, I showed how to make the Draftail editor toolbar sticky, how to use the WAGTAILADMIN_RICH_TEXT_EDITORS setting to define editors with feature sets and introduced how to add new features.

In the following "Extending the Draftail Editor" series I'll cover:

  1. How to add inline styles to the editor interface using both element tags and custom styles, how to use a unicode glyph label as a button icon, and then create a freeform icon using the path of an svg file.
  2. How to block styles and give two options for approaching the issue of adding text alignment to your rich text areas. Which route you choose has a big impact on your data structure. Ideally, you should choose which before building out your content. Migrating from one to the other could require a fair amount of work.
  3. Enabling dynamic inline text using a dummy element class and JavaScript.

  Please feel free to leave any questions or comments below, or send me a message here