I am using wagtail in headless mode with tailwind/nextjs.
When i add RichTextBlock and choose h2,
In API response we get data as :
{
"type": "text",
"value": "<h2 data-block-key=\"owi81\">Repurpose Article into Social Media Content</h2>",
"id": "2b7e811d-eb1b-40da-b108-8cf9778536b3"
}
I wish to be able to use tailwind classes so output could be:
{
"type": "text",
"value": "<h2 data-block-key=\"owi81\" class=\"text-5xl\"></h2>",
"id": "2b7e811d-eb1b-40da-b108-8cf9778536b3"
}
I tried wagtail hooks but couldn't really make things work!
@hooks.register('register_rich_text_features')
def register_tailwind_classes(features):
feature_name = 'h2'
type_ = 'header-two'
tag = 'h2'
# 4.configure the content transform from the DB to the editor and back.
db_conversion = {
'from_database_format': {tag: TailwindClassHandler(type_)},
'to_database_format': {'block_map': {type_: tag}},
}
# 5. Call register_converter_rule to register the content transformation conversion.
features.register_converter_rule('contentstate', feature_name, db_conversion)
How can we achieve this?
Is it even the right way!
I am using wagtail in headless mode with tailwind/nextjs.
When i add RichTextBlock and choose h2,
In API response we get data as :
{
"type": "text",
"value": "<h2 data-block-key=\"owi81\">Repurpose Article into Social Media Content</h2>",
"id": "2b7e811d-eb1b-40da-b108-8cf9778536b3"
}
I wish to be able to use tailwind classes so output could be:
{
"type": "text",
"value": "<h2 data-block-key=\"owi81\" class=\"text-5xl\"></h2>",
"id": "2b7e811d-eb1b-40da-b108-8cf9778536b3"
}
I tried wagtail hooks but couldn't really make things work!
@hooks.register('register_rich_text_features')
def register_tailwind_classes(features):
feature_name = 'h2'
type_ = 'header-two'
tag = 'h2'
# 4.configure the content transform from the DB to the editor and back.
db_conversion = {
'from_database_format': {tag: TailwindClassHandler(type_)},
'to_database_format': {'block_map': {type_: tag}},
}
# 5. Call register_converter_rule to register the content transformation conversion.
features.register_converter_rule('contentstate', feature_name, db_conversion)
How can we achieve this?
Is it even the right way!
2 Answers
Reset to default 1I prefer to keep headings as their own block with an extra field to provide an optional anchor target.
If you do keep it in the rich text block, I would drop the built-in H2 format and create a custom one that adds the css class to the output. This is done in the features.register_converter_rule()
method.
I use the following to register block features in Draftail:
def register_block_feature(
features,
feature_name,
type_,
description,
css_class,
element="div",
wrapper=None,
label=None,
icon=None,
editor_style=None,
):
control = {
"type": type_,
"description": description,
"element": element,
}
if label:
control["label"] = label
elif icon:
control["icon"] = icon
else:
control["label"] = description
if editor_style:
control["style"] = editor_style
features.register_editor_plugin(
"draftail",
feature_name,
draftail_features.BlockFeature(control, css={"all": ["draftail-editor.css"]}),
)
block_map = {"element": element, "props": {"class": css_class}}
if wrapper:
block_map["wrapper"] = wrapper
features.register_converter_rule(
"contentstate",
feature_name,
{
"from_database_format": {
f"{element}[class={css_class}]": BlockElementHandler(type_)
},
"to_database_format": {
"block_map": {
type_: block_map
}
},
},
)
Then your hook would be something like:
@hooks.register('register_rich_text_features')
def register_tailwind_h2_feature(features):
register_block_feature(
features=features,
feature_name='tw-h2',
type_='tw-h2',
description='H2',
css_class='text-5xl',
element='h2',
icon='h2'
)
Finally, in your editor feature list, swap h2
for tw-h2
. There's more info on this topic in these articles.
The one drawback on this is that you lose the markdown support for h2
(by typing ##
) - the draftail docs lack any information on registering/modifying keyboard and markdown shortcuts unfortunately. I'd like to find out info on this if anyone has it.
Instead of using heading elements within the rich text block, give them their own block type in the StreamField definition:
body = StreamField([
("paragraph", blocks.RichTextBlock()),
("heading", blocks.CharBlock()),
# ... other blocks
])
That way, you can define exactly what markup to give to the heading within your client-side code, instead of having to fiddle with the API response to include presentational class
attributes. (After all, the whole reason you chose to use Wagtail in headless mode was to keep presentational logic fully on the client side, presumably...?)