From 794fe25e978c3f481f0ff5902f515a78c9212db1 Mon Sep 17 00:00:00 2001
From: Sage Abdullah <sage.abdullah@torchbox.com>
Date: Thu, 23 Jan 2025 10:05:09 +0000
Subject: [PATCH] Add normalize() implementation to TypedTableBlock (#12808)

---
 CHANGELOG.txt                               |  1 +
 docs/releases/6.5.md                        |  2 +-
 wagtail/contrib/typed_table_block/blocks.py |  5 ++++
 wagtail/contrib/typed_table_block/tests.py  | 27 +++++++++++++++++++++
 4 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 0aaf55774e..bc6fc8f740 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -5,6 +5,7 @@ Changelog
 ~~~~~~~~~~~~~~~~
 
  * Add `WAGTAIL_` prefix to Wagtail-specific tag settings (Aayushman Singh)
+ * Implement `normalize` on `TypedTableBlock` to assist with setting `default` and `preview_value` (Sage Abdullah)
  * Fix: Take preferred language into account for translatable strings in client-side code (Bernhard Bliem, Sage Abdullah)
  * Docs: Add missing `django.contrib.admin` to list of apps in "add to Django project" guide (Mohamed Rabiaa)
 
diff --git a/docs/releases/6.5.md b/docs/releases/6.5.md
index e77e5375ce..f8e5118c6e 100644
--- a/docs/releases/6.5.md
+++ b/docs/releases/6.5.md
@@ -13,7 +13,7 @@ depth: 1
 
 ### Other features
 
- * ...
+ * Implement `normalize` on `TypedTableBlock` to assist with setting `default` and `preview_value` (Sage Abdullah)
 
 ### Bug fixes
 
diff --git a/wagtail/contrib/typed_table_block/blocks.py b/wagtail/contrib/typed_table_block/blocks.py
index ef4304aaca..c7b045a4dc 100644
--- a/wagtail/contrib/typed_table_block/blocks.py
+++ b/wagtail/contrib/typed_table_block/blocks.py
@@ -188,6 +188,11 @@ class BaseTypedTableBlock(Block):
                 "caption": "",
             }
 
+    def normalize(self, value):
+        if value is None or isinstance(value, TypedTable):
+            return value
+        return self.to_python(value)
+
     def to_python(self, value):
         if value:
             columns = [
diff --git a/wagtail/contrib/typed_table_block/tests.py b/wagtail/contrib/typed_table_block/tests.py
index 38bada6c77..b28194f3a3 100644
--- a/wagtail/contrib/typed_table_block/tests.py
+++ b/wagtail/contrib/typed_table_block/tests.py
@@ -162,6 +162,33 @@ class TestTableBlock(TestCase):
             ["fr", "68000000", "A large country with baguettes"],
         )
 
+    def test_normalize(self):
+        # Should be able to handle JSONish data from the database, which can be
+        # useful when defining a default value for a TypedTableBlock
+        table = self.block.normalize(self.db_data)
+        self.assertEqual(table.caption, "Countries and their food")
+        self.assertIsInstance(table, TypedTable)
+        self.assertEqual(len(table.columns), 2)
+        self.assertEqual(table.columns[0]["heading"], "Country")
+        self.assertEqual(table.columns[1]["heading"], "Description")
+        rows = list(table.rows)
+        self.assertEqual(len(rows), 2)
+        self.assertEqual(
+            [block.value for block in rows[0]],
+            ["nl", "A small country with stroopwafels"],
+        )
+        self.assertEqual(
+            [block.value for block in rows[1]], ["fr", "A large country with baguettes"]
+        )
+
+        # For a TypedTable instance, normalize should return the instance as-is
+        normalized_table = self.block.normalize(table)
+        self.assertIs(normalized_table, table)
+
+        # Should normalize None as-is
+        none_value = self.block.normalize(None)
+        self.assertIs(none_value, None)
+
     def test_to_python(self):
         """
         Test that we can turn JSONish data from the database into a TypedTable instance