diff --git a/wagtail/admin/templates/wagtailadmin/generic/streamfield_block_preview.html b/wagtail/admin/templates/wagtailadmin/generic/streamfield_block_preview.html
index d1c3d14d1b..d31a32f472 100644
--- a/wagtail/admin/templates/wagtailadmin/generic/streamfield_block_preview.html
+++ b/wagtail/admin/templates/wagtailadmin/generic/streamfield_block_preview.html
@@ -18,7 +18,11 @@
{% block content %}
- {% include_block bound_block %}
+ {% if block_def.is_previewable %}
+ {% include_block bound_block %}
+ {% else %}
+ {% translate 'Preview not available' %}
+ {% endif %}
{% endblock %}
diff --git a/wagtail/admin/tests/test_block_preview.py b/wagtail/admin/tests/test_block_preview.py
index c7422c0a4f..18ec713dbd 100644
--- a/wagtail/admin/tests/test_block_preview.py
+++ b/wagtail/admin/tests/test_block_preview.py
@@ -82,7 +82,7 @@ class TestStreamFieldBlockPreviewView(WagtailTestUtils, TestCase):
)
self.user.save()
- block = blocks.CharBlock()
+ block = blocks.CharBlock(preview_value="Hello, world!")
response = self.get(block)
self.assertEqual(response.status_code, 200)
@@ -93,7 +93,7 @@ class TestStreamFieldBlockPreviewView(WagtailTestUtils, TestCase):
soup = self.get_soup(response.content)
main = soup.select_one("main")
self.assertIsNotNone(main)
- self.assertEqual(main.text.strip(), "None")
+ self.assertEqual(main.text.strip(), "Preview not available")
def test_preview_value_falls_back_to_default(self):
block = blocks.IntegerBlock(default=42)
diff --git a/wagtail/blocks/base.py b/wagtail/blocks/base.py
index 8a1072939a..d79c6233fa 100644
--- a/wagtail/blocks/base.py
+++ b/wagtail/blocks/base.py
@@ -293,6 +293,25 @@ class Block(metaclass=BaseBlock):
return self.normalize(self.meta.preview_value)
return self.get_default()
+ @cached_property
+ def is_previewable(self):
+ # To prevent showing a broken preview if the block preview has not been
+ # configured, consider the block to be previewable if either:
+ # - a preview template, preview value, or default value is defined
+ # - a preview method is overridden
+ # which are the intended ways to configure block previews.
+ #
+ # If a block is made previewable by other means, the `is_previewable`
+ # property should be overridden to return `True`.
+ return (
+ hasattr(self.meta, "preview_template")
+ or hasattr(self.meta, "preview_value")
+ or getattr(self.meta, "default", None) is not None
+ or self.__class__.get_preview_context is not Block.get_preview_context
+ or self.__class__.get_preview_template is not Block.get_preview_template
+ or self.__class__.get_preview_value is not Block.get_preview_value
+ )
+
def get_description(self):
return getattr(self.meta, "description", "")
diff --git a/wagtail/blocks/field_block.py b/wagtail/blocks/field_block.py
index cbe4a7d4dd..93ae3d272b 100644
--- a/wagtail/blocks/field_block.py
+++ b/wagtail/blocks/field_block.py
@@ -117,6 +117,7 @@ class FieldBlockAdapter(Adapter):
"required": block.required,
"icon": block.meta.icon,
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"classname": " ".join(classname),
"showAddCommentButton": getattr(
block.field.widget, "show_add_comment_button", True
diff --git a/wagtail/blocks/list_block.py b/wagtail/blocks/list_block.py
index c78537a3c1..f446eb4f15 100644
--- a/wagtail/blocks/list_block.py
+++ b/wagtail/blocks/list_block.py
@@ -452,6 +452,7 @@ class ListBlockAdapter(Adapter):
"description": block.get_description(),
"icon": block.meta.icon,
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"classname": block.meta.form_classname,
"collapsed": block.meta.collapsed,
"strings": {
diff --git a/wagtail/blocks/static_block.py b/wagtail/blocks/static_block.py
index 3224c8b9e6..8c26ef3150 100644
--- a/wagtail/blocks/static_block.py
+++ b/wagtail/blocks/static_block.py
@@ -60,6 +60,7 @@ class StaticBlockAdapter(Adapter):
"label": block.label,
"description": block.get_description(),
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
},
]
diff --git a/wagtail/blocks/stream_block.py b/wagtail/blocks/stream_block.py
index 5b894ce468..42de7c9ad1 100644
--- a/wagtail/blocks/stream_block.py
+++ b/wagtail/blocks/stream_block.py
@@ -830,6 +830,7 @@ class StreamBlockAdapter(Adapter):
"required": block.required,
"icon": block.meta.icon,
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"classname": block.meta.form_classname,
"maxNum": block.meta.max_num,
"minNum": block.meta.min_num,
diff --git a/wagtail/blocks/struct_block.py b/wagtail/blocks/struct_block.py
index fcca424f9e..05a717cb8c 100644
--- a/wagtail/blocks/struct_block.py
+++ b/wagtail/blocks/struct_block.py
@@ -408,6 +408,7 @@ class StructBlockAdapter(Adapter):
"required": block.required,
"icon": block.meta.icon,
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"classname": block.meta.form_classname,
}
diff --git a/wagtail/contrib/table_block/tests.py b/wagtail/contrib/table_block/tests.py
index 04476d0a7f..853d845851 100644
--- a/wagtail/contrib/table_block/tests.py
+++ b/wagtail/contrib/table_block/tests.py
@@ -573,6 +573,7 @@ class TestTableBlockForm(WagtailTestUtils, SimpleTestCase):
"required": True,
"icon": "table",
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"classname": "w-field w-field--char_field w-field--table_input",
"showAddCommentButton": True,
"strings": {"ADD_COMMENT": "Add Comment"},
diff --git a/wagtail/contrib/typed_table_block/blocks.py b/wagtail/contrib/typed_table_block/blocks.py
index dc6fd5023e..ef4304aaca 100644
--- a/wagtail/contrib/typed_table_block/blocks.py
+++ b/wagtail/contrib/typed_table_block/blocks.py
@@ -336,6 +336,7 @@ class TypedTableBlockAdapter(Adapter):
"required": block.required,
"icon": block.meta.icon,
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"strings": {
"CAPTION": _("Caption"),
"CAPTION_HELP_TEXT": _(
diff --git a/wagtail/contrib/typed_table_block/tests.py b/wagtail/contrib/typed_table_block/tests.py
index b7a41b0234..38bada6c77 100644
--- a/wagtail/contrib/typed_table_block/tests.py
+++ b/wagtail/contrib/typed_table_block/tests.py
@@ -239,6 +239,7 @@ class TestTableBlock(TestCase):
"required": False,
"icon": "table",
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"strings": {
"CAPTION": "Caption",
"CAPTION_HELP_TEXT": (
diff --git a/wagtail/snippets/tests/test_snippets.py b/wagtail/snippets/tests/test_snippets.py
index e8b397f209..438764133c 100644
--- a/wagtail/snippets/tests/test_snippets.py
+++ b/wagtail/snippets/tests/test_snippets.py
@@ -5544,6 +5544,7 @@ class TestSnippetChooserBlock(TestCase):
"required": True,
"icon": "snippet",
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"helpText": "pick an advert, any advert",
"classname": "w-field w-field--model_choice_field w-field--admin_snippet_chooser",
"showAddCommentButton": True,
@@ -5856,6 +5857,7 @@ class TestSnippetChooserBlockWithCustomPrimaryKey(TestCase):
"required": True,
"icon": "snippet",
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"helpText": "pick an advert, any advert",
"classname": "w-field w-field--model_choice_field w-field--admin_snippet_chooser",
"showAddCommentButton": True,
diff --git a/wagtail/tests/test_blocks.py b/wagtail/tests/test_blocks.py
index 3ca6c045ab..b5cfe53777 100644
--- a/wagtail/tests/test_blocks.py
+++ b/wagtail/tests/test_blocks.py
@@ -62,6 +62,32 @@ class TestBlock(SimpleTestCase):
block = blocks.Block()
self.assertIs(blocks.Block.definition_registry[block.definition_prefix], block)
+ def test_block_is_previewable(self):
+ class CustomContextBlock(blocks.Block):
+ def get_preview_context(self, value, parent_context=None):
+ return {"value": value, "foo": "bar"}
+
+ class CustomTemplateBlock(blocks.Block):
+ def get_preview_template(self, value=None, context=None):
+ return "foo.html"
+
+ class CustomValueBlock(blocks.Block):
+ def get_preview_value(self):
+ return "foo"
+
+ cases = [
+ (blocks.Block(), False),
+ (blocks.Block(preview_template="foo.html"), True),
+ (blocks.Block(preview_value="foo"), True),
+ (blocks.Block(default="bar"), True),
+ (CustomContextBlock(), True),
+ (CustomTemplateBlock(), True),
+ (CustomValueBlock(), True),
+ ]
+ for block, is_previewable in cases:
+ with self.subTest(block=block):
+ self.assertEqual(block.is_previewable, is_previewable)
+
class TestFieldBlock(WagtailTestUtils, SimpleTestCase):
def test_charfield_render(self):
@@ -100,6 +126,7 @@ class TestFieldBlock(WagtailTestUtils, SimpleTestCase):
"required": True,
"icon": "placeholder",
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"classname": "w-field w-field--char_field w-field--text_input",
"showAddCommentButton": True,
"strings": {"ADD_COMMENT": "Add Comment"},
@@ -208,6 +235,7 @@ class TestFieldBlock(WagtailTestUtils, SimpleTestCase):
"required": True,
"icon": "placeholder",
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"classname": "w-field w-field--choice_field w-field--select",
"showAddCommentButton": True,
"strings": {"ADD_COMMENT": "Add Comment"},
@@ -619,6 +647,7 @@ class TestRichTextBlock(TestCase):
"label": "Test richtextblock",
"description": "",
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"required": True,
"showAddCommentButton": True,
"strings": {"ADD_COMMENT": "Add Comment"},
@@ -643,6 +672,7 @@ class TestRichTextBlock(TestCase):
"required": True,
"icon": "pilcrow",
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"classname": "w-field w-field--char_field w-field--draftail_rich_text_area",
"showAddCommentButton": False, # Draftail manages its own comments
"strings": {"ADD_COMMENT": "Add Comment"},
@@ -667,6 +697,7 @@ class TestRichTextBlock(TestCase):
"required": True,
"icon": "pilcrow",
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"classname": "w-field w-field--char_field w-field--draftail_rich_text_area",
"showAddCommentButton": False, # Draftail manages its own comments
"strings": {"ADD_COMMENT": "Add Comment"},
@@ -784,6 +815,7 @@ class TestChoiceBlock(WagtailTestUtils, SimpleTestCase):
"required": True,
"icon": "placeholder",
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"classname": "w-field w-field--choice_field w-field--select",
"showAddCommentButton": True,
"strings": {"ADD_COMMENT": "Add Comment"},
@@ -1187,6 +1219,7 @@ class TestMultipleChoiceBlock(WagtailTestUtils, SimpleTestCase):
"required": True,
"icon": "placeholder",
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"classname": "w-field w-field--multiple_choice_field w-field--select_multiple",
"showAddCommentButton": True,
"strings": {"ADD_COMMENT": "Add Comment"},
@@ -1625,6 +1658,7 @@ class TestRawHTMLBlock(unittest.TestCase):
"required": True,
"icon": "code",
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"classname": "w-field w-field--char_field w-field--textarea",
"showAddCommentButton": True,
"strings": {"ADD_COMMENT": "Add Comment"},
@@ -1969,6 +2003,7 @@ class TestStructBlock(SimpleTestCase):
"required": False,
"icon": "placeholder",
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"classname": "struct-block",
},
)
@@ -2000,6 +2035,7 @@ class TestStructBlock(SimpleTestCase):
"required": False,
"icon": "placeholder",
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"classname": "struct-block",
"formTemplate": "Hello
",
},
@@ -2026,6 +2062,7 @@ class TestStructBlock(SimpleTestCase):
"required": False,
"icon": "placeholder",
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"classname": "struct-block",
"formTemplate": "Hello
",
},
@@ -2061,6 +2098,7 @@ class TestStructBlock(SimpleTestCase):
"required": False,
"icon": "placeholder",
"blockDefId": block.definition_prefix,
+ "isPreviewable": block.is_previewable,
"classname": "struct-block",
"helpIcon": (
'