We have setup our CMS to allow some dynamic values (like, number of products we offer) to be included as inline links in structured texts. So, these are inline-links but their result are simple strings. It would be great if we could allow the structure text to format these values. Seems like we can’t include the links in formatted blocks. A workaround I’ve thought of is to update the renderer such that it formats the item based on the surrounding items. But for that, I’d need the function passed renderInlineRecord to receive as parameters the ancestors or siblings of the record. Currently, it only receives the record itself.
Custom node rules functions do get the ancestors, so I suppose it’s not too hard to include it in renderInlineRecord as well. These don’t go through the inline records, apparently.
I’d like the following 2,000 to be part of an anchor like the item after it.
This custom node rule will you let parse out the parent and siblings:
import {
type Document,
type InlineItem,
type InlineNode,
isInlineItem,
isParagraph,
isRoot,
type Paragraph,
Span,
} from 'datocms-structured-text-utils';
customNodeRules={[
// Only apply to paragraphs
renderNodeRule(isParagraph, ({ node, ancestors }) => {
// Only apply to top-level ones
if (isRoot(ancestors[0])) {
return (
<>
{node.children.flatMap((inlineNode, index, siblings) => {
if (isInlineItem(inlineNode)) {
const recordContent = page.structuredText.links.find(
(link) => link.id === inlineNode.item,
)!;
const prevSibling = siblings[index - 1] as Span; // In real usage, you'd check their types and respond accordingly
const nextSibling = siblings[index + 1] as Span;
return [
<a href="https://example.com">
{prevSibling.value}
{recordContent.title}
{nextSibling.value}
</a>,
];
} else {
return [];
}
})}
</>
);
}
}),
]}
Will get you this:
It’s not very user-friendly, but the prev/next siblings would just be the inline item’s index +/- 1, and you can look up the inline item’s actual context in your structured text field response’s links prop.
You have to do it with customNodeRules because that affects the parent node, vs renderInlineRecord’s individual child nodes (by then it’s too late; they can’t affect their siblings because each one is mapped through and rendered separately by the their shared parent node, which is what the node rule affects).
Very interesting workaround, Roger. I knew there had to be some way to do it, haha.
To be sure if I understood, in this case, I would basically have to apply everything within this rule. Like, if I have any other rules, I should move them within the implementation of this rule, and I should have the renderInlineLink function return null. Is that correct?
I think that is one way to do it, yes, but you can also add a bunch of conditionals as you see fit, if that’s easier, e.g. use renderInlineRecords for most things, but in customNodeRule, you can expand the isRoot or isInlineItem checks to include model API keys, certain template strings, field names, whatever you need.
It’s not going to the cleanest implementation either way, so make sure to comment it well, but it should work.
I still think this is something that would be nice to have as a built-in without needing workarounds, but that depends on how many people end up needing it.
In the meantime, I hope this works for you! If you run into any trouble during implementation, let us know.