diff --git a/wagtail/admin/rich_text/converters/html_to_contentstate.py b/wagtail/admin/rich_text/converters/html_to_contentstate.py
index 9d5ce3d28..ea1c07a5f 100644
--- a/wagtail/admin/rich_text/converters/html_to_contentstate.py
+++ b/wagtail/admin/rich_text/converters/html_to_contentstate.py
@@ -103,7 +103,13 @@ class InlineStyleElementHandler:
self.style = style
def handle_starttag(self, name, attrs, state, contentstate):
- assert state.current_block is not None, "%s element found at the top level" % name
+ if state.current_block is None:
+ # Inline style element encountered at the top level -
+ # start a new paragraph block to contain it
+ block = Block('unstyled', depth=state.list_depth)
+ contentstate.blocks.append(block)
+ state.current_block = block
+ state.leading_whitespace = STRIP_WHITESPACE
if state.leading_whitespace == FORCE_WHITESPACE:
# any pending whitespace should be output before handling this tag,
@@ -131,7 +137,13 @@ class InlineEntityElementHandler:
self.entity_type = entity_type
def handle_starttag(self, name, attrs, state, contentstate):
- assert state.current_block is not None, "%s element found at the top level" % name
+ if state.current_block is None:
+ # Inline entity element encountered at the top level -
+ # start a new paragraph block to contain it
+ block = Block('unstyled', depth=state.list_depth)
+ contentstate.blocks.append(block)
+ state.current_block = block
+ state.leading_whitespace = STRIP_WHITESPACE
if state.leading_whitespace == FORCE_WHITESPACE:
# any pending whitespace should be output before handling this tag,
diff --git a/wagtail/admin/tests/test_contentstate.py b/wagtail/admin/tests/test_contentstate.py
index 8c3c9f6dd..3c277fa5d 100644
--- a/wagtail/admin/tests/test_contentstate.py
+++ b/wagtail/admin/tests/test_contentstate.py
@@ -144,6 +144,24 @@ class TestHtmlToContentState(TestCase):
]
})
+ def test_inline_styles_at_start_of_bare_block(self):
+ converter = ContentstateConverter(features=['bold', 'italic'])
+ result = json.loads(converter.from_database_format(
+ '''Seriously, stop talking about Fight Club already.'''
+ ))
+ self.assertContentStateEqual(result, {
+ 'entityMap': {},
+ 'blocks': [
+ {
+ 'inlineStyleRanges': [
+ {'offset': 0, 'length': 9, 'style': 'BOLD'},
+ {'offset': 30, 'length': 10, 'style': 'ITALIC'},
+ ],
+ 'text': 'Seriously, stop talking about Fight Club already.', 'depth': 0, 'type': 'unstyled', 'key': '00000', 'entityRanges': []
+ },
+ ]
+ })
+
def test_inline_styles_depend_on_features(self):
converter = ContentstateConverter(features=['italic', 'just-made-it-up'])
result = json.loads(converter.from_database_format(
@@ -237,6 +255,44 @@ class TestHtmlToContentState(TestCase):
]
})
+ def test_link_in_bare_text(self):
+ converter = ContentstateConverter(features=['link'])
+ result = json.loads(converter.from_database_format(
+ '''an external link'''
+ ))
+ self.assertContentStateEqual(result, {
+ 'entityMap': {
+ '0': {'mutability': 'MUTABLE', 'type': 'LINK', 'data': {'url': 'http://wagtail.io'}}
+ },
+ 'blocks': [
+ {
+ 'inlineStyleRanges': [], 'text': 'an external link', 'depth': 0, 'type': 'unstyled', 'key': '00000',
+ 'entityRanges': [{'offset': 3, 'length': 8, 'key': 0}]
+ },
+ ]
+ })
+
+ def test_link_at_start_of_bare_text(self):
+ converter = ContentstateConverter(features=['link'])
+ result = json.loads(converter.from_database_format(
+ '''an external link and another'''
+ ))
+ self.assertContentStateEqual(result, {
+ 'entityMap': {
+ '0': {'mutability': 'MUTABLE', 'type': 'LINK', 'data': {'url': 'http://wagtail.io'}},
+ '1': {'mutability': 'MUTABLE', 'type': 'LINK', 'data': {'url': 'http://torchbox.com'}},
+ },
+ 'blocks': [
+ {
+ 'inlineStyleRanges': [], 'text': 'an external link and another', 'depth': 0, 'type': 'unstyled', 'key': '00000',
+ 'entityRanges': [
+ {'offset': 0, 'length': 16, 'key': 0},
+ {'offset': 21, 'length': 7, 'key': 1},
+ ]
+ },
+ ]
+ })
+
def test_page_link(self):
converter = ContentstateConverter(features=['link'])
result = json.loads(converter.from_database_format(