wagtailheadless-cmsrichtextblock

Applying CSS classes to RichTextblock in wagtails StreamField API Response


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!


Solution

  • I 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.