Skip to content

Commit 7024116

Browse files
dsolistorresclaude
andcommitted
fix(block-editor): render subscript and superscript marks as <sub>/<sup> in Story Block HTML output
The renderMarks macro in VM_global_library.vm handled bold, italic, strike, underline, and link marks but silently dropped subscript and superscript marks even though the Block Editor (TipTap) registers both extensions and authors can produce them. Adds the two missing branches in both the opening and closing reverse-range loops so nested combinations (e.g. bold + superscript) close in the correct order, plus an integration test in StoryBlockMapTest covering each mark individually and a combined nesting case. Closes #35460 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 855c7d5 commit 7024116

2 files changed

Lines changed: 39 additions & 0 deletions

File tree

dotCMS/src/main/webapp/WEB-INF/velocity/VM_global_library.vm

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@
2626
*##if ($mark.type == "underline")#*
2727
*#<u>#*
2828
*##end#*
29+
*##if ($mark.type == "subscript")#*
30+
*#<sub>#*
31+
*##end#*
32+
*##if ($mark.type == "superscript")#*
33+
*#<sup>#*
34+
*##end#*
2935
*##if ($mark.type == "link")#*
3036
*#<a href="${esc.html($mark.attrs.href)}" target="${esc.html($mark.attrs.target)}"#*
3137
*##if($mark.attrs.title) title="${esc.html($mark.attrs.title)}"#end#*
@@ -54,6 +60,12 @@
5460
*##if ($mark.type == "underline")#*
5561
*#</u>#*
5662
*##end#*
63+
*##if ($mark.type == "subscript")#*
64+
*#</sub>#*
65+
*##end#*
66+
*##if ($mark.type == "superscript")#*
67+
*#</sup>#*
68+
*##end#*
5769
*##if($mark.type == "link")#*
5870
*#</a>#*
5971
*##end#*

dotcms-integration/src/test/java/com/dotcms/rendering/velocity/viewtools/content/StoryBlockMapTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ public class StoryBlockMapTest extends IntegrationTestBase {
5757
private static final String JSON_ULIST =
5858
"{\"type\":\"doc\",\"content\":[{\"type\":\"bulletList\",\"content\":[{\"type\":\"listItem\",\"attrs\":{\"textAlign\":\"left\"},\"content\":[{\"type\":\"paragraph\",\"attrs\":{\"textAlign\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"1\"}]}]},{\"type\":\"listItem\",\"attrs\":{\"textAlign\":\"left\"},\"content\":[{\"type\":\"paragraph\",\"attrs\":{\"textAlign\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"2\"}]}]},{\"type\":\"listItem\",\"attrs\":{\"textAlign\":\"left\"},\"content\":[{\"type\":\"paragraph\",\"attrs\":{\"textAlign\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"3\"}]}]}]}]}";
5959

60+
private static final String JSON_SUB_SUP =
61+
"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"attrs\":{\"textAlign\":\"left\"},\"content\":["
62+
+ "{\"type\":\"text\",\"marks\":[{\"type\":\"superscript\"}],\"text\":\"sup\"},"
63+
+ "{\"type\":\"text\",\"text\":\" and \"},"
64+
+ "{\"type\":\"text\",\"marks\":[{\"type\":\"subscript\"}],\"text\":\"sub\"},"
65+
+ "{\"type\":\"text\",\"text\":\" and \"},"
66+
+ "{\"type\":\"text\",\"marks\":[{\"type\":\"bold\"},{\"type\":\"superscript\"}],\"text\":\"bold-sup\"}"
67+
+ "]}]}";
68+
6069
private static final String MACRO = "#macro(renderMarks $content)\n" +
6170
"\n" +
6271
" #if($content.marks)\n" +
@@ -256,6 +265,24 @@ public void test_default_render_to_html() throws JSONException {
256265

257266
}
258267

268+
/**
269+
* Method to test: {@link StoryBlockMap#toHtml()}
270+
* Given Scenario: A paragraph contains text with subscript, superscript, and a combined bold+superscript mark.
271+
* ExpectedResult: The rendered HTML wraps each text run in {@code <sub>} / {@code <sup>} tags, and combined
272+
* marks nest with the correct closing order (e.g. {@code <strong><sup>bold-sup</sup></strong>}).
273+
*/
274+
@Test
275+
public void test_subscript_and_superscript_marks_render_to_html() throws JSONException {
276+
277+
final StoryBlockMap storyBlockMap = new StoryBlockMap(JSON_SUB_SUP);
278+
final String html = storyBlockMap.toHtml();
279+
280+
Assert.assertTrue(html + ": missing <sup>sup</sup>", html.contains("<sup>sup</sup>"));
281+
Assert.assertTrue(html + ": missing <sub>sub</sub>", html.contains("<sub>sub</sub>"));
282+
Assert.assertTrue(html + ": missing <strong><sup>bold-sup</sup></strong>",
283+
html.contains("<strong><sup>bold-sup</sup></strong>"));
284+
}
285+
259286
/**
260287
* Method to test: {@link StoryBlockMap#toHtml()}
261288
* Given Scenario: Will parse the json paragraph and render the custom html

0 commit comments

Comments
 (0)