diff --git a/wagtail/admin/rich_text/converters/html_to_contentstate.py b/wagtail/admin/rich_text/converters/html_to_contentstate.py index 9d5ce3d288..ea1c07a5f2 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 8c3c9f6dda..3c277fa5d1 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( + '''<b>Seriously</b>, stop talking about <i>Fight Club</i> 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 <a href="http://wagtail.io">external</a> 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( + '''<a href="http://wagtail.io">an external link</a> and <a href="http://torchbox.com">another</a>''' + )) + 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(