Repository: wagtail/wagtail

Issue:
styleguide - custom icons with id defined with single quotes cause 500 error when loading style guide
### Issue Summary

If a registered custom icon uses single quotes to define the `id`, the `all_icons` context variable doesn't recognise the icon name (i.e. the name attribute is empty). These icons behave as expected elsewhere in the admin interface.

When the styleguide template loops through the custom icons, the icon template tag is called with an empty name attribute causing the 500 error ([ref](https://github.com/wagtail/wagtail/blob/9dc38076ed9d12d63dc40258e6135b43d4054d96/wagtail/contrib/styleguide/templates/wagtailstyleguide/base.html#L524)).
```django
{% icon name=item.name classname="w-w-8 w-h-8" %}
```

### Steps to Reproduce

Add a custom svg to your project with the id defined with single quotes:

```xml
<svg
   viewBox="0 0 1024 1024"
   id='icon-ban'
   xmlns="http://www.w3.org/2000/svg"
   xmlns:svg="http://www.w3.org/2000/svg">
   <path
     d="M 734.4,825 199,289.6 C 154.2,352.2 128,429 128,512 c 0,212 172,384 384,384 83,0 159.8,-26.2 222.4,-71 z M 825,734.4 C 869.8,671.8 896,595 896,512 896,300 724,128 512,128 429,128 352.2,154.2 289.6,199 Z M 0,512 a 512,512 0 1 1 1024,0 512,512 0 1 1 -1024,0 z"
     style="stroke-width:2" 
   />
</svg>
```

[Register the icon](https://docs.wagtail.org/en/stable/advanced_topics/icons.html#add-a-custom-icon) as per docs.

Browse the style guide - the error is thrown in the icon template tag due to missing icon name.

-   I have confirmed that this issue can be reproduced as described on a fresh Wagtail project: Yes

### Technical details

-   Python version: 3.1.2
-   Django version: 4.2.7
-   Wagtail version: 6.0.2
-   Browser version: Chrome 124



Discussion:

Resolving patch:
diff --git a/wagtail/contrib/styleguide/views.py b/wagtail/contrib/styleguide/views.py
--- a/wagtail/contrib/styleguide/views.py
+++ b/wagtail/contrib/styleguide/views.py
@@ -101,7 +101,10 @@ def media(self):
         )
 
 
-icon_id_pattern = re.compile(r"id=\"icon-([a-z0-9-]+)\"")
+# Allow single and double quotes for the ID.
+# For simplicity and readability, we don't enforce the opening
+# and closing quotes to match.
+icon_id_pattern = re.compile(r"""id=["']icon-([a-z0-9-]+)["']""")
 icon_comment_pattern = re.compile(r"<!--!(.*?)-->")
 
 


Test patch:
diff --git a/wagtail/contrib/styleguide/tests.py b/wagtail/contrib/styleguide/tests.py
--- a/wagtail/contrib/styleguide/tests.py
+++ b/wagtail/contrib/styleguide/tests.py
@@ -21,3 +21,36 @@ def test_styleguide(self):
         self.assertContains(response, custom_css)
         self.assertContains(response, widget_css)
         self.assertContains(response, widget_js)
+
+    def test_icons(self):
+        def register_icons(icons):
+            return icons + [
+                "tests/icons/single-quotes.svg",  # id='icon-single-quotes'
+            ]
+
+        with self.register_hook("register_icons", register_icons):
+            response = self.client.get(reverse("wagtailstyleguide"))
+
+        self.assertEqual(response.status_code, 200)
+        # Should render the icons in the table
+        self.assertContains(
+            response,
+            '<use href="#icon-single-quotes"></use>',
+            html=True,
+        )
+        self.assertContains(
+            response,
+            "<td>Custom icon with single quotes for the id</td>",
+            html=True,
+        )
+        # Built-in icon, not from the above hook
+        self.assertContains(
+            response,
+            '<use href="#icon-h1"></use>',
+            html=True,
+        )
+        self.assertContains(
+            response,
+            "<td>Custom icon</td>",
+            html=True,
+        )
diff --git a/wagtail/test/testapp/templates/tests/icons/single-quotes.svg b/wagtail/test/testapp/templates/tests/icons/single-quotes.svg
new file mode 100644
--- /dev/null
+++ b/wagtail/test/testapp/templates/tests/icons/single-quotes.svg
@@ -0,0 +1 @@
+<svg id='icon-single-quotes' xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><!--! Custom icon with single quotes for the id --></svg>