feat(attestation): add tiptap json to html converter
This commit is contained in:
parent
9e2662cb34
commit
09ce6ee1c8
2 changed files with 207 additions and 0 deletions
65
app/services/tiptap_service.rb
Normal file
65
app/services/tiptap_service.rb
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
class TiptapService
|
||||||
|
class << self
|
||||||
|
def to_html(node, tags)
|
||||||
|
children(node[:content], tags)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def children(content, tags)
|
||||||
|
content.map { node_to_html(_1, tags) }.join
|
||||||
|
end
|
||||||
|
|
||||||
|
def node_to_html(node, tags)
|
||||||
|
case node
|
||||||
|
in type: 'paragraph', content:, **rest
|
||||||
|
"<p#{text_align(rest[:attrs])}>#{children(content, tags)}</p>"
|
||||||
|
in type: 'heading', attrs: { level:, **attrs }, content:
|
||||||
|
"<h#{level}#{text_align(attrs)}>#{children(content, tags)}</h#{level}>"
|
||||||
|
in type: 'bulletList', content:
|
||||||
|
"<ul>#{children(content, tags)}</ul>"
|
||||||
|
in type: 'orderedList', content:
|
||||||
|
"<ol>#{children(content, tags)}</ol>"
|
||||||
|
in type: 'listItem', content:
|
||||||
|
"<li>#{children(content, tags)}</li>"
|
||||||
|
in type: 'text', text:, **rest
|
||||||
|
if rest[:marks].present?
|
||||||
|
apply_marks(text, rest[:marks])
|
||||||
|
else
|
||||||
|
text
|
||||||
|
end
|
||||||
|
in type: 'mention', attrs: { id: }, **rest
|
||||||
|
if rest[:marks].present?
|
||||||
|
apply_marks(tags[id], rest[:marks])
|
||||||
|
else
|
||||||
|
tags[id]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def text_align(attrs)
|
||||||
|
if attrs.present? && attrs[:textAlign].present?
|
||||||
|
" style=\"text-align: #{attrs[:textAlign]}\""
|
||||||
|
else
|
||||||
|
""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def apply_marks(text, marks)
|
||||||
|
marks.reduce(text) do |text, mark|
|
||||||
|
case mark
|
||||||
|
in type: 'bold'
|
||||||
|
"<strong>#{text}</strong>"
|
||||||
|
in type: 'italic'
|
||||||
|
"<em>#{text}</em>"
|
||||||
|
in type: 'underline'
|
||||||
|
"<u>#{text}</u>"
|
||||||
|
in type: 'strike'
|
||||||
|
"<s>#{text}</s>"
|
||||||
|
in type: 'highlight'
|
||||||
|
"<mark>#{text}</mark>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
142
spec/services/tiptap_service_spec.rb
Normal file
142
spec/services/tiptap_service_spec.rb
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
RSpec.describe TiptapService do
|
||||||
|
describe '.to_html' do
|
||||||
|
let(:json) do
|
||||||
|
{
|
||||||
|
type: 'doc',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
attrs: { textAlign: 'right' },
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: 'Hello world!'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: 'Bonjour ',
|
||||||
|
marks: [{ type: 'italic' }, { type: 'strike' }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'mention',
|
||||||
|
attrs: { id: 'name' },
|
||||||
|
marks: [{ type: 'bold' }, { type: 'underline' }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: ' '
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: '!',
|
||||||
|
marks: [{ type: 'highlight' }]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'heading',
|
||||||
|
attrs: { level: 1 },
|
||||||
|
content: [{ type: 'text', text: 'Heading 1' }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'heading',
|
||||||
|
attrs: { level: 2, textAlign: 'center' },
|
||||||
|
content: [{ type: 'text', text: 'Heading 2' }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'heading',
|
||||||
|
attrs: { level: 3 },
|
||||||
|
content: [{ type: 'text', text: 'Heading 3' }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'bulletList',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'listItem',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: 'Item 1'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'listItem',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: 'Item 2'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'orderedList',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'listItem',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: 'Item 1'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'listItem',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: 'Item 2'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
let(:tags) { { 'name' => 'Paul' } }
|
||||||
|
let(:html) do
|
||||||
|
[
|
||||||
|
'<p style="text-align: right">Hello world!</p>',
|
||||||
|
'<p><s><em>Bonjour </em></s><u><strong>Paul</strong></u> <mark>!</mark></p>',
|
||||||
|
'<h1>Heading 1</h1>',
|
||||||
|
'<h2 style="text-align: center">Heading 2</h2>',
|
||||||
|
'<h3>Heading 3</h3>',
|
||||||
|
'<ul><li><p>Item 1</p></li><li><p>Item 2</p></li></ul>',
|
||||||
|
'<ol><li><p>Item 1</p></li><li><p>Item 2</p></li></ol>'
|
||||||
|
].join
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns html' do
|
||||||
|
expect(described_class.to_html(json, tags)).to eq(html)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue