The Draftail editor is the default Richtext editor in Wagtail 2.x. While there is some documentation on creating complex extensions, but not a lot of examples of basic custom in-line styles. It took me a bit to figure out how to get that to work, so I wanted to share some examples of custom inline styles.
An in-line style, as far as Draftail goes, is one where you are wrapping some text with tags. Out of the box in-line styles include ‘bold’, and ‘italic’. We’re also covering styles here that don’t need to collect user input, and just work with a single button click.
I based these on the examples from the Wagtail documentation.
Our first example just applies a class tag to the selected text using a span. This is a common use case where you just want to format text in a particular custom way.
This code is placed in your wagtail_hooks.py
file:
from wagtail.core import hooks
from wagtail.admin.rich_text.converters.html_to_contentstate \
import InlineStyleElementHandler
import wagtail.admin.rich_text.editors.draftail.features \
as draftail_features@hooks.register('register_rich_text_features')
def register_smallcaption_feature(features):
"""
Registering the 'smallcaption' Draftail feature which
adds a span around the selected text
with its class set to 'small-caption'
""" # 1. Set up variables for use below
feature_name = 'smallcaption'
type_ = 'SMALLCAPTION' # 2. Set up a dictionary to pass to Draftail to configure
# how it handles this feature in its toolbar.
# The 'style' attribute controls how Draftail formats that text
# in the editor - does not affect the final rendered HTML
# In this case, I am adding similar formatting to what
# the CSS will do to that 'small-caption' class in the template control = {
'type': type_,
'label': 'SC',
'description': 'Small Caption',
'style': {
'font-size': '75%',
'color': '#666'
}
} # 3. Register the configuration for Draftail to know about.
# This makes it available to add to editing toolbars,
# but the RichTextField(features=[]) list has to include it to
# actually make it appear in the toolbar features.register_editor_plugin(
'draftail',
feature_name,
draftail_features.InlineStyleFeature(control)
) # 4.configure the content transform
# from the DB to the editor and back. # The "From" version uses a CSS selector to find spans with
# a class of 'small-caption'
# The "To" version adds a span with a class of 'small-caption'
# surrounding the selected text db_conversion = {
'from_database_format': {
'span[class="small-caption"]':
InlineStyleElementHandler(type_)
},
'to_database_format': {
'style_map': {type_: 'span class="small-caption"'}
},
} # 5. Call register_converter_rule to register
# the content transformation conversion rule with Draftail features.register_converter_rule(
'contentstate',
feature_name,
db_conversion
)
The key to this example is the db_conversion setup. In there, we tell it what to wrap around the selected text — in this case a <span class="small-caption">
. We also tell it how to find text tagged this way in the HTML using the span[class="small-caption"]
CSS selector.
Also interesting is the ‘style’ entry in the ‘control’ setup — where we can have the editor add some styling when text is tagged with our plugin. In this case, we are roughly mimicking what the CSS will do to our class. You can also inject your stylesheet into the editor if you just want to actually use the same styles, but I’ve found that to have too many unintended styling conflict consequences.
Next is a very similar idea, but using the style tag directly, if you just have some simple styling that you want to include.
This code is placed in your wagtail_hooks.py
file:
# 1. Use the register_rich_text_features hook.
@hooks.register('register_rich_text_features')
def register_underline_feature(features):
"""
Registering the `underline` feature.
"""
feature_name = 'underline'
type_ = 'UNDERLINE'
# 2. Configure how Draftail handles the feature in its toolbar.
control = {
'type': type_,
'label': 'U',
'description': 'underline',
}
# 3. Call register_editor_plugin to register the configuration for Draftail.
features.register_editor_plugin(
'draftail', feature_name,
draftail_features.InlineStyleFeature(control)
)
# 4.configure the content transform from the DB and back.
db_conversion = {
'from_database_format': {
'span[style="text-decoration: underline"]':
InlineStyleElementHandler(type_)},
'to_database_format': {
'style_map': {
type_: 'span style="text-decoration: underline"'}},
}
# 5. Call register_converter_rule to register the conversion.
features.register_converter_rule('contentstate', feature_name, db_conversion)
In truth, other than underline, (and even maybe in this case) you likely want to use CSS classes instead of inline styles, but here is how you could do it. Note that we don’t need to specify the style
attribute on the control
because the actual tag we are surrounding the text with has everything we need to see the effect in the editor.
The ctrl-U
keyboard binding will work for this particular extension because there is an existing Draftail extension named underline that binds that key. We could use that more directly by using the ‘u’ tag instead of our span with inline style, but the u tag has been deprecated as ‘underline’ for a long time.
These examples show entering the code directly in the wagtail_hooks.py
file, but you can obviously make yourself a reusable file and import the function into wagtail_hooks.py.
In order to get these to actually appear in the edit bar of your Draftail Richtext field, you’ll need to include them in its features
list:
class HomePage(Page):
"""
HomePage Page model - the body field has the features set
to include our custom extensions, as well as bold.
"""
body = RichTextField(features=[
'bold',
'underline',
'smallcaption'
],
null=True, blank=True)
content_panels = Page.content_panels + [
FieldPanel('body', classname="full"),
]
This example only includes three features — you’ll need to include all the features that you want to enable.
Check out this GitHub Repo with these examples in a runnable project.
I hope this saves you some time, cheers!