Skip to content

Commit e6967a4

Browse files
committed
Removes unnecessary newlines from rich text as JSON delivery API output (#19391)
* Removes unnecessary newlines from rich text as JSON delivery API output. * Fix case from PR feedback. # Conflicts: # src/Umbraco.Infrastructure/DeliveryApi/ApiRichTextElementParser.cs # tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/RichTextParserTests.cs
1 parent b87ae6c commit e6967a4

File tree

2 files changed

+61
-3
lines changed

2 files changed

+61
-3
lines changed

src/Umbraco.Infrastructure/DeliveryApi/ApiRichTextElementParser.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ private T ParseElement<T>(HtmlNode element, IPublishedContentCache contentCache,
9898
// - non-#comment nodes
9999
// - non-#text nodes
100100
// - non-empty #text nodes
101-
// - empty #text between inline elements (see #17037)
101+
// - empty #text between inline elements (see #17037) but not #text with only newlines (see #19388)
102102
HtmlNode[] childNodes = element.ChildNodes
103-
.Where(c => c.Name != CommentNodeName && (c.Name != TextNodeName || c.NextSibling is not null || string.IsNullOrWhiteSpace(c.InnerText) is false))
103+
.Where(c => c.Name != CommentNodeName && (c.Name != TextNodeName || IsNonEmptyElement(c)))
104104
.ToArray();
105105

106106
var tag = TagName(element);
@@ -121,6 +121,9 @@ private T ParseElement<T>(HtmlNode element, IPublishedContentCache contentCache,
121121
return createElement(tag, attributes, childElements);
122122
}
123123

124+
private static bool IsNonEmptyElement(HtmlNode htmlNode) =>
125+
string.IsNullOrWhiteSpace(htmlNode.InnerText) is false || htmlNode.InnerText.Any(c => c != '\n' && c != '\r');
126+
124127
private static string TagName(HtmlNode htmlNode) => htmlNode.Name;
125128

126129
private void ReplaceLocalLinks(IPublishedContentCache contentCache, IPublishedMediaCache mediaCache, Dictionary<string, object> attributes)

tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/RichTextParserTests.cs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,16 +357,71 @@ public void ParseElement_CanHandleMixedInlineAndBlockLevelBlocks()
357357
Assert.IsEmpty(blockLevelBlock.Elements);
358358
}
359359

360+
private const string TestParagraph = "What follows from <strong>here</strong> <em>is</em> <a href=\"#\">just</a> a bunch of text.";
361+
360362
[Test]
361363
public void ParseElement_CanHandleWhitespaceAroundInlineElemements()
362364
{
363365
var parser = CreateRichTextElementParser();
364366

365-
var element = parser.Parse("<p>What follows from <strong>here</strong> <em>is</em> <a href=\"#\">just</a> a bunch of text.</p>", RichTextBlockModel.Empty) as RichTextRootElement;
367+
var element = parser.Parse($"<p>{TestParagraph}</p>", RichTextBlockModel.Empty) as RichTextRootElement;
366368
Assert.IsNotNull(element);
367369
var paragraphElement = element.Elements.Single() as RichTextGenericElement;
368370
Assert.IsNotNull(paragraphElement);
369371

372+
AssertTestParagraph(paragraphElement);
373+
}
374+
375+
[TestCase(1, "\n")]
376+
[TestCase(2, "\n")]
377+
[TestCase(1, "\r")]
378+
[TestCase(2, "\r")]
379+
[TestCase(1, "\r\n")]
380+
[TestCase(2, "\r\n")]
381+
public void ParseElement_RemovesNewLinesAroundHtmlStructuralElements(int numberOfNewLineCharacters, string newlineCharacter)
382+
{
383+
var parser = CreateRichTextElementParser();
384+
385+
var newLineSeparator = string.Concat(Enumerable.Repeat(newlineCharacter, numberOfNewLineCharacters));
386+
var element = parser.Parse($"<table>{newLineSeparator}<tr>{newLineSeparator}<td>{TestParagraph}</td>{newLineSeparator}</tr>{newLineSeparator}</table>", RichTextBlockModel.Empty) as RichTextRootElement;
387+
Assert.IsNotNull(element);
388+
var tableElement = element.Elements.Single() as RichTextGenericElement;
389+
Assert.IsNotNull(tableElement);
390+
391+
var rowElement = tableElement.Elements.Single() as RichTextGenericElement;
392+
Assert.IsNotNull(rowElement);
393+
394+
var cellElement = rowElement.Elements.Single() as RichTextGenericElement;
395+
Assert.IsNotNull(cellElement);
396+
397+
AssertTestParagraph(cellElement);
398+
}
399+
400+
[TestCase(1, "\n")]
401+
[TestCase(2, "\n")]
402+
[TestCase(1, "\r")]
403+
[TestCase(2, "\r")]
404+
[TestCase(1, "\r\n")]
405+
[TestCase(2, "\r\n")]
406+
public void ParseElement_RemovesNewLinesAroundHtmlContentElements(int numberOfNewLineCharacters, string newlineCharacter)
407+
{
408+
var parser = CreateRichTextElementParser();
409+
410+
var newLineSeparator = string.Concat(Enumerable.Repeat(newlineCharacter, numberOfNewLineCharacters));
411+
var element = parser.Parse($"<div><p>{TestParagraph}</p>{newLineSeparator}<p></p>{newLineSeparator}<p>&nbsp;</p>{newLineSeparator}<p>{TestParagraph}</p></div>", RichTextBlockModel.Empty) as RichTextRootElement;
412+
Assert.IsNotNull(element);
413+
var divElement = element.Elements.Single() as RichTextGenericElement;
414+
Assert.IsNotNull(divElement);
415+
416+
var paragraphELements = divElement.Elements;
417+
Assert.AreEqual(4, paragraphELements.Count());
418+
419+
AssertTestParagraph(paragraphELements.First() as RichTextGenericElement);
420+
AssertTestParagraph(paragraphELements.Last() as RichTextGenericElement);
421+
}
422+
423+
private static void AssertTestParagraph(RichTextGenericElement paragraphElement)
424+
{
370425
var childElements = paragraphElement.Elements.ToArray();
371426
Assert.AreEqual(7, childElements.Length);
372427

0 commit comments

Comments
 (0)