Slate Rendering

To add tooltips to block text for glossary terms I want to hook into the rendering of text blocks. I want to insert per matching word a Semantic-UI Popup component or something similar as an abbr node.
A block transformer (IBlockFieldSerializationTransformer) on backend side would be handsome but is not appropriate as it changes the block content permanently. I want the block text / value to be untouched.

How to hook into block rendering?

I see several options:

  • you could do both a block serialization transformer and a deserialization transformer, so that the change is removed on deserialization
  • you could wrap the Slate block TextBlockView in your own block and mutate the value that's passed to the slate block renderer. Basically something like:
const TextBlockView = config.blocks.blocksConfig.slate.view;
config.blocks.blocksConfig.slate.view = (props) => {
    const {data} = props;
    const value = mutateValue(data.value);   // implement mutateValue
    return <TextBlockView {...props} data={{...data, value}} />
}

The difficulty will be implementing that mutateValue() function, as you'll want to mark some of the text as a "mark" or an "inline element", then implement the appropriate slate element component.

  • another option that I see would be to implement the above text block view component replacement trick, but instead of mutating the slate value, you use reactdom and render to an html string, then you're dealing with a plain HTML value which might be something that can be handled by some glossary libraries.

Thanks for your detailed answer. That confirms the way I took.
In an already customized TextBlockView for other purposes I use a customized serializeNodes method to parse the text and replace glossary term matches with Popup components. No extra slate element component but splitting the text and yielding span or Popup inside of <Leaf path={path} leaf={node} text={node} mode="view" key={path}>{node.text}</Leaf>

The only thing left to do is to make this replacement configurable as the tooltips should show up only on a part of the site.
GitHub - rohberg/volto-slate-glossary: Glossary with tooltips (working but still WIP)

OMG, yet another volto-slate plugin! And asyncPropExtenders "in the wild"... Really exciting to see this stuff put to use.

So, inspecting the code here: volto-slate-glossary/utils.js at 73ff650948520adc75d18850011d550ffe4cf538 · rohberg/volto-slate-glossary · GitHub I see a more straight-forward solution:

What if we make the Element and Leaf configurable through slate config? So then, instead of wraping the TextBlockView, you could wrap and pre-process the "leaf value" and insert the appropriate leaf markers for the leaf? volto-slate/render.jsx at 5b9a4ec5c19eb7e0f8c0a0f2822b7de53d46867a · eea/volto-slate · GitHub. Then the Leaf component would be able to use an appropriately configured leaf renderer component volto-slate/config.jsx at 5b9a4ec5c19eb7e0f8c0a0f2822b7de53d46867a · eea/volto-slate · GitHub

As for making it per-site section... special cases are always ugly and I don't have a good suggestion. Worst case scenario, make a "condition function" that is configurable and executed by each leaf... I think the performance would be fine, I wouldn't worry about that, but it feels suboptimal.

@ksuess Another option would be to use the eea.volto.slate package and override the eea.volto.slate/block.py at main · eea/eea.volto.slate · GitHub Notice it has support for "magical methods" handle_<node_type>, like eea.volto.slate/block.py at main · eea/eea.volto.slate · GitHub So you could implement this server-side somewhat easy in the transformers.

Warning though, eea.volto.slate is WIP.