Convert html blocks inside table cells
Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
@@ -580,7 +580,7 @@ func (c *converter) convertTableCells(row ast.Node, cellType NodeType) ([]Node,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
inlineContent, err := c.convertInlineChildren(child)
|
content, err := c.convertTableCellContent(child)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -598,13 +598,86 @@ func (c *converter) convertTableCells(row ast.Node, cellType NodeType) ([]Node,
|
|||||||
cells = append(cells, Node{
|
cells = append(cells, Node{
|
||||||
Type: cellType,
|
Type: cellType,
|
||||||
Attrs: attrs,
|
Attrs: attrs,
|
||||||
Content: []Node{{Type: NodeParagraph, Content: inlineContent}},
|
Content: content,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return cells, nil
|
return cells, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *converter) convertTableCellContent(cell ast.Node) ([]Node, error) {
|
||||||
|
if c.cellHasBlockHTML(cell) {
|
||||||
|
raw := c.collectCellRawContent(cell)
|
||||||
|
nodes, err := convertProseMirrorFromHTMLBlock(raw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(nodes) > 0 {
|
||||||
|
return nodes, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inlineContent, err := c.convertInlineChildren(cell)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return []Node{{Type: NodeParagraph, Content: inlineContent}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *converter) cellHasBlockHTML(cell ast.Node) bool {
|
||||||
|
for ch := cell.FirstChild(); ch != nil; ch = ch.NextSibling() {
|
||||||
|
if ch.Kind() != ast.KindRawHTML {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
raw := ch.(*ast.RawHTML)
|
||||||
|
for i := 0; i < raw.Segments.Len(); i++ {
|
||||||
|
seg := raw.Segments.At(i)
|
||||||
|
val := strings.ToLower(string(seg.Value(c.source)))
|
||||||
|
if containsBlockOpenTag(val) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func containsBlockOpenTag(s string) bool {
|
||||||
|
for _, tag := range []string{
|
||||||
|
"<ul", "<ol", "<table", "<blockquote", "<pre", "<div",
|
||||||
|
"<h1", "<h2", "<h3", "<h4", "<h5", "<h6", "<hr",
|
||||||
|
} {
|
||||||
|
if strings.Contains(s, tag) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *converter) collectCellRawContent(cell ast.Node) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for ch := cell.FirstChild(); ch != nil; ch = ch.NextSibling() {
|
||||||
|
switch ch.Kind() {
|
||||||
|
case ast.KindRawHTML:
|
||||||
|
raw := ch.(*ast.RawHTML)
|
||||||
|
for i := 0; i < raw.Segments.Len(); i++ {
|
||||||
|
seg := raw.Segments.At(i)
|
||||||
|
buf.Write(seg.Value(c.source))
|
||||||
|
}
|
||||||
|
case ast.KindText:
|
||||||
|
t := ch.(*ast.Text)
|
||||||
|
buf.Write(t.Segment.Value(c.source))
|
||||||
|
if t.SoftLineBreak() {
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
}
|
||||||
|
case ast.KindString:
|
||||||
|
buf.Write(ch.(*ast.String).Value)
|
||||||
|
default:
|
||||||
|
buf.WriteString(c.extractText(ch))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
// extractText recursively collects the text content of all descendant nodes.
|
// extractText recursively collects the text content of all descendant nodes.
|
||||||
func (c *converter) extractText(n ast.Node) string {
|
func (c *converter) extractText(n ast.Node) string {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|||||||
@@ -644,6 +644,55 @@ func TestParseMarkdown_MarkdownTableWithInlineMarks(t *testing.T) {
|
|||||||
assert.Equal(t, MarkEm, p.Content[2].Marks[0].Type)
|
assert.Equal(t, MarkEm, p.Content[2].Marks[0].Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseMarkdown_MarkdownTableCellWithBlockList(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
md := "| Header |\n| --- |\n| <ul><li>one</li><li>two</li></ul> |\n"
|
||||||
|
doc, err := ParseMarkdown(md)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, doc.Content, 1)
|
||||||
|
assert.Equal(t, NodeTable, doc.Content[0].Type)
|
||||||
|
|
||||||
|
dataRow := doc.Content[0].Content[1]
|
||||||
|
assert.Equal(t, NodeTableRow, dataRow.Type)
|
||||||
|
cell := dataRow.Content[0]
|
||||||
|
assert.Equal(t, NodeTableCell, cell.Type)
|
||||||
|
|
||||||
|
require.Len(t, cell.Content, 1)
|
||||||
|
assert.Equal(t, NodeBulletList, cell.Content[0].Type)
|
||||||
|
require.Len(t, cell.Content[0].Content, 2)
|
||||||
|
assert.Equal(t, NodeListItem, cell.Content[0].Content[0].Type)
|
||||||
|
assert.Equal(t, NodeListItem, cell.Content[0].Content[1].Type)
|
||||||
|
|
||||||
|
li1Para := cell.Content[0].Content[0].Content[0]
|
||||||
|
require.Equal(t, NodeParagraph, li1Para.Type)
|
||||||
|
assert.Equal(t, "one", *li1Para.Content[0].Text)
|
||||||
|
|
||||||
|
li2Para := cell.Content[0].Content[1].Content[0]
|
||||||
|
require.Equal(t, NodeParagraph, li2Para.Type)
|
||||||
|
assert.Equal(t, "two", *li2Para.Content[0].Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseMarkdown_MarkdownTableCellWithOrderedList(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
md := "| Header |\n| --- |\n| <ol><li>first</li><li>second</li></ol> |\n"
|
||||||
|
doc, err := ParseMarkdown(md)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, doc.Content, 1)
|
||||||
|
assert.Equal(t, NodeTable, doc.Content[0].Type)
|
||||||
|
|
||||||
|
dataRow := doc.Content[0].Content[1]
|
||||||
|
cell := dataRow.Content[0]
|
||||||
|
assert.Equal(t, NodeTableCell, cell.Type)
|
||||||
|
|
||||||
|
require.Len(t, cell.Content, 1)
|
||||||
|
assert.Equal(t, NodeOrderedList, cell.Content[0].Type)
|
||||||
|
require.Len(t, cell.Content[0].Content, 2)
|
||||||
|
assert.Equal(t, NodeListItem, cell.Content[0].Content[0].Type)
|
||||||
|
assert.Equal(t, NodeListItem, cell.Content[0].Content[1].Type)
|
||||||
|
}
|
||||||
|
|
||||||
func TestParseMarkdown_BlockHTMLScriptRemovedKeepsSafeContent(t *testing.T) {
|
func TestParseMarkdown_BlockHTMLScriptRemovedKeepsSafeContent(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user