kopia lustrzana https://github.com/wagtail/wagtail
Add docs and tests for customising snippet icons
rodzic
c2d4f89692
commit
198388bf55
|
@ -79,6 +79,7 @@ Viewsets are Wagtail's mechanism for defining a group of related admin views wit
|
|||
```{eval-rst}
|
||||
.. autoclass:: wagtail.snippets.views.snippets.SnippetViewSet
|
||||
|
||||
.. autoattribute:: icon
|
||||
.. autoattribute:: list_display
|
||||
.. autoattribute:: filterset_class
|
||||
.. autoattribute:: index_view_class
|
||||
|
|
|
@ -567,7 +567,11 @@ class MemberFilterSet(WagtailFilterSet):
|
|||
fields = ["shirt_size"]
|
||||
```
|
||||
|
||||
You can define a {attr}`~wagtail.snippets.views.snippets.SnippetViewSet.list_display` attribute to specify the columns shown on the listing view. You can also add the ability to filter the listing view by defining a {attr}`~wagtail.snippets.views.snippets.SnippetViewSet.filterset_class` attribute on a subclass of `SnippetViewSet`. For example:
|
||||
You can define a {attr}`~wagtail.snippets.views.snippets.SnippetViewSet.icon` attribute to specify the icon that is used across the admin for this snippet type. The `icon` needs to be [registered in the Wagtail icon library](../../advanced_topics/icons). If `icon` is not set, the default `"snippet"` icon is used.
|
||||
|
||||
The {attr}`~wagtail.snippets.views.snippets.SnippetViewSet.list_display` attribute can be set to specify the columns shown on the listing view. You can also add the ability to filter the listing view by defining a {attr}`~wagtail.snippets.views.snippets.SnippetViewSet.filterset_class` attribute on a subclass of `SnippetViewSet`.
|
||||
|
||||
For example:
|
||||
|
||||
```python
|
||||
# views.py
|
||||
|
@ -578,6 +582,7 @@ from myapp.models import MemberFilterSet
|
|||
|
||||
|
||||
class MemberViewSet(SnippetViewSet):
|
||||
icon = "user"
|
||||
list_display = ["name", "shirt_size", "get_shirt_size_display", UpdatedAtColumn()]
|
||||
filterset_class = MemberFilterSet
|
||||
```
|
||||
|
|
|
@ -4,13 +4,13 @@ from django.test import TestCase
|
|||
from django.urls import reverse
|
||||
|
||||
from wagtail.snippets.bulk_actions.delete import DeleteBulkAction
|
||||
from wagtail.test.snippets.models import StandardSnippet
|
||||
from wagtail.test.testapp.models import FullFeaturedSnippet
|
||||
from wagtail.test.utils import WagtailTestUtils
|
||||
|
||||
|
||||
class TestSnippetDeleteView(WagtailTestUtils, TestCase):
|
||||
def setUp(self):
|
||||
self.snippet_model = StandardSnippet
|
||||
self.snippet_model = FullFeaturedSnippet
|
||||
|
||||
# create a set of test snippets
|
||||
self.test_snippets = [
|
||||
|
@ -41,6 +41,9 @@ class TestSnippetDeleteView(WagtailTestUtils, TestCase):
|
|||
self.assertTemplateUsed(
|
||||
response, "wagtailsnippets/bulk_actions/confirm_bulk_delete.html"
|
||||
)
|
||||
self.assertTemplateUsed(response, "wagtailadmin/shared/header.html")
|
||||
self.assertEqual(response.context["header_icon"], "cog")
|
||||
self.assertContains(response, "icon icon-cog", count=1)
|
||||
|
||||
def test_bulk_delete(self):
|
||||
response = self.client.post(self.url)
|
||||
|
@ -66,7 +69,7 @@ class TestSnippetDeleteView(WagtailTestUtils, TestCase):
|
|||
|
||||
html = response.content.decode()
|
||||
self.assertInHTML(
|
||||
"<p>You don't have permission to delete these standard snippets</p>",
|
||||
"<p>You don't have permission to delete these full-featured snippets</p>",
|
||||
html,
|
||||
)
|
||||
|
||||
|
|
|
@ -111,6 +111,7 @@ class TestSnippetListView(WagtailTestUtils, TestCase):
|
|||
response = self.get()
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "wagtailsnippets/snippets/type_index.html")
|
||||
self.assertEqual(response.context["header_icon"], "snippet")
|
||||
|
||||
def get_with_limited_permissions(self):
|
||||
self.user.is_superuser = False
|
||||
|
@ -3783,6 +3784,7 @@ class TestSnippetChooserPanel(WagtailTestUtils, TestCase):
|
|||
self.assertIn(self.advert_text, field_html)
|
||||
self.assertIn("Choose advert", field_html)
|
||||
self.assertIn("Choose another advert", field_html)
|
||||
self.assertIn("icon icon-snippet icon", field_html)
|
||||
|
||||
def test_render_as_empty_field(self):
|
||||
test_snippet = SnippetChooserModel()
|
||||
|
@ -4683,6 +4685,7 @@ class TestAddOnlyPermissions(WagtailTestUtils, TestCase):
|
|||
response = self.client.get(reverse("wagtailsnippets_tests_advert:add"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "wagtailsnippets/snippets/create.html")
|
||||
self.assertEqual(response.context["header_icon"], "snippet")
|
||||
|
||||
def test_get_edit(self):
|
||||
response = self.client.get(
|
||||
|
@ -4746,6 +4749,7 @@ class TestEditOnlyPermissions(WagtailTestUtils, TestCase):
|
|||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "wagtailsnippets/snippets/edit.html")
|
||||
self.assertEqual(response.context["header_icon"], "snippet")
|
||||
|
||||
def test_get_delete(self):
|
||||
response = self.client.get(
|
||||
|
@ -4807,6 +4811,7 @@ class TestDeleteOnlyPermissions(WagtailTestUtils, TestCase):
|
|||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "wagtailadmin/generic/confirm_delete.html")
|
||||
self.assertEqual(response.context["header_icon"], "snippet")
|
||||
|
||||
|
||||
class TestSnippetEditHandlers(WagtailTestUtils, TestCase):
|
||||
|
@ -5276,6 +5281,8 @@ class TestSnippetChooseWithCustomPrimaryKey(WagtailTestUtils, TestCase):
|
|||
def test_simple(self):
|
||||
response = self.get()
|
||||
self.assertTemplateUsed(response, "wagtailadmin/generic/chooser/chooser.html")
|
||||
self.assertEqual(response.context["header_icon"], "snippet")
|
||||
self.assertEqual(response.context["icon"], "snippet")
|
||||
|
||||
def test_ordering(self):
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
from django.contrib.admin.utils import quote
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from wagtail.admin.panels import get_edit_handler
|
||||
from wagtail.coreutils import get_dummy_request
|
||||
from wagtail.models import Workflow, WorkflowContentType
|
||||
from wagtail.test.testapp.models import Advert, FullFeaturedSnippet, SnippetChooserModel
|
||||
from wagtail.test.utils import WagtailTestUtils
|
||||
|
||||
|
||||
class TestCustomIcon(WagtailTestUtils, TestCase):
|
||||
def setUp(self):
|
||||
self.user = self.login()
|
||||
self.object = FullFeaturedSnippet.objects.create(
|
||||
text="test snippet with custom icon"
|
||||
)
|
||||
self.revision_1 = self.object.save_revision()
|
||||
self.revision_1.publish()
|
||||
self.object.text = "test snippet with custom icon (updated)"
|
||||
self.revision_2 = self.object.save_revision()
|
||||
|
||||
def get_url(self, url_name, args=()):
|
||||
app_label = self.object._meta.app_label
|
||||
model_name = self.object._meta.model_name
|
||||
view_name = f"wagtailsnippets_{app_label}_{model_name}:{url_name}"
|
||||
return reverse(view_name, args=args)
|
||||
|
||||
def test_get_views(self):
|
||||
pk = quote(self.object.pk)
|
||||
views = [
|
||||
("list", []),
|
||||
("add", []),
|
||||
("edit", [pk]),
|
||||
("delete", [pk]),
|
||||
("usage", [pk]),
|
||||
("unpublish", [pk]),
|
||||
("workflow_history", [pk]),
|
||||
("revisions_revert", [pk, self.revision_1.id]),
|
||||
("revisions_compare", [pk, self.revision_1.id, self.revision_2.id]),
|
||||
("revisions_unschedule", [pk, self.revision_2.id]),
|
||||
]
|
||||
for view_name, args in views:
|
||||
with self.subTest(view_name=view_name):
|
||||
response = self.client.get(self.get_url(view_name, args))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.context["header_icon"], "cog")
|
||||
self.assertContains(response, "icon icon-cog", count=1)
|
||||
# TODO: Make the list view use the shared header template
|
||||
if view_name != "list":
|
||||
self.assertTemplateUsed(response, "wagtailadmin/shared/header.html")
|
||||
|
||||
def test_get_history(self):
|
||||
response = self.client.get(self.get_url("history", [quote(self.object.pk)]))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "wagtailadmin/shared/header.html")
|
||||
# History view icon is not configurable for consistency with pages
|
||||
self.assertEqual(response.context["header_icon"], "history")
|
||||
self.assertContains(response, "icon icon-history")
|
||||
self.assertNotContains(response, "icon icon-cog")
|
||||
|
||||
def test_get_workflow_history_detail(self):
|
||||
# Assign default workflow to the snippet model
|
||||
self.content_type = ContentType.objects.get_for_model(type(self.object))
|
||||
self.workflow = Workflow.objects.first()
|
||||
WorkflowContentType.objects.create(
|
||||
content_type=self.content_type,
|
||||
workflow=self.workflow,
|
||||
)
|
||||
self.object.text = "Edited!"
|
||||
self.object.save_revision()
|
||||
workflow_state = self.workflow.start(self.object, self.user)
|
||||
response = self.client.get(
|
||||
self.get_url(
|
||||
"workflow_history_detail", [quote(self.object.pk), workflow_state.id]
|
||||
)
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "wagtailadmin/shared/header.html")
|
||||
# The icon is not displayed in the header,
|
||||
# but it is displayed in the main content
|
||||
self.assertEqual(response.context["header_icon"], "list-ul")
|
||||
self.assertContains(response, "icon icon-list-ul")
|
||||
self.assertContains(response, "icon icon-cog")
|
||||
|
||||
|
||||
class TestSnippetChooserPanelWithIcon(WagtailTestUtils, TestCase):
|
||||
def setUp(self):
|
||||
self.user = self.login()
|
||||
self.request = get_dummy_request()
|
||||
self.request.user = self.user
|
||||
self.text = "Test full-featured snippet with icon text"
|
||||
test_snippet = SnippetChooserModel.objects.create(
|
||||
advert=Advert.objects.create(text="foo"),
|
||||
full_featured=FullFeaturedSnippet.objects.create(text=self.text),
|
||||
)
|
||||
|
||||
self.edit_handler = get_edit_handler(SnippetChooserModel)
|
||||
self.form_class = self.edit_handler.get_form_class()
|
||||
form = self.form_class(instance=test_snippet)
|
||||
edit_handler = self.edit_handler.get_bound_panel(
|
||||
instance=test_snippet, form=form, request=self.request
|
||||
)
|
||||
|
||||
self.object_chooser_panel = [
|
||||
panel
|
||||
for panel in edit_handler.children
|
||||
if getattr(panel, "field_name", None) == "full_featured"
|
||||
][0]
|
||||
|
||||
def test_render_html(self):
|
||||
field_html = self.object_chooser_panel.render_html()
|
||||
self.assertIn(self.text, field_html)
|
||||
self.assertIn("Choose full-featured snippet", field_html)
|
||||
self.assertIn("Choose another full-featured snippet", field_html)
|
||||
self.assertIn("icon icon-cog icon", field_html)
|
||||
|
||||
# make sure no snippet icons remain
|
||||
self.assertNotIn("icon-snippet", field_html)
|
||||
|
||||
def test_render_as_empty_field(self):
|
||||
test_snippet = SnippetChooserModel()
|
||||
form = self.form_class(instance=test_snippet)
|
||||
edit_handler = self.edit_handler.get_bound_panel(
|
||||
instance=test_snippet, form=form, request=self.request
|
||||
)
|
||||
|
||||
snippet_chooser_panel = [
|
||||
panel
|
||||
for panel in edit_handler.children
|
||||
if getattr(panel, "field_name", None) == "full_featured"
|
||||
][0]
|
||||
|
||||
field_html = snippet_chooser_panel.render_html()
|
||||
self.assertIn("Choose full-featured snippet", field_html)
|
||||
self.assertIn("Choose another full-featured snippet", field_html)
|
||||
self.assertIn("icon icon-cog icon", field_html)
|
||||
|
||||
# make sure no snippet icons remain
|
||||
self.assertNotIn("icon-snippet", field_html)
|
||||
|
||||
def test_chooser_popup(self):
|
||||
response = self.client.get(
|
||||
reverse("wagtailsnippetchoosers_tests_fullfeaturedsnippet:choose")
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.context["header_icon"], "cog")
|
||||
self.assertContains(response, "icon icon-cog", count=1)
|
||||
self.assertEqual(response.context["icon"], "cog")
|
||||
|
||||
# make sure no snippet icons remain
|
||||
for key in response.context.keys():
|
||||
if "icon" in key:
|
||||
self.assertNotIn("snippet", response.context[key])
|
|
@ -0,0 +1,24 @@
|
|||
# Generated by Django 4.1.7 on 2023-03-13 16:44
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("tests", "0022_variousondeletemodel"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="snippetchoosermodel",
|
||||
name="full_featured",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="tests.fullfeaturedsnippet",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1135,9 +1135,6 @@ class FullFeaturedSnippet(
|
|||
verbose_name_plural = "full-featured snippets"
|
||||
|
||||
|
||||
register_snippet(FullFeaturedSnippet)
|
||||
|
||||
|
||||
def get_default_advert():
|
||||
return Advert.objects.first()
|
||||
|
||||
|
@ -1343,9 +1340,13 @@ class EventPageChooserModel(models.Model):
|
|||
|
||||
class SnippetChooserModel(models.Model):
|
||||
advert = models.ForeignKey(Advert, help_text="help text", on_delete=models.CASCADE)
|
||||
full_featured = models.ForeignKey(
|
||||
FullFeaturedSnippet, on_delete=models.CASCADE, null=True, blank=True
|
||||
)
|
||||
|
||||
panels = [
|
||||
FieldPanel("advert"),
|
||||
FieldPanel("full_featured"),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -12,8 +12,10 @@ from wagtail.admin.ui.components import Component
|
|||
from wagtail.admin.views.account import BaseSettingsPanel
|
||||
from wagtail.admin.widgets import Button
|
||||
from wagtail.snippets.models import register_snippet
|
||||
from wagtail.snippets.views.snippets import SnippetViewSet
|
||||
from wagtail.test.snippets.models import FilterableSnippet
|
||||
from wagtail.test.snippets.views import FilterableSnippetViewSet
|
||||
from wagtail.test.testapp.models import FullFeaturedSnippet
|
||||
|
||||
from .forms import FavouriteColourForm
|
||||
|
||||
|
@ -226,3 +228,10 @@ def add_broken_links_summary_item(request, items):
|
|||
|
||||
|
||||
register_snippet(FilterableSnippet, viewset=FilterableSnippetViewSet)
|
||||
|
||||
|
||||
class FullFeaturedSnippetViewSet(SnippetViewSet):
|
||||
icon = "cog"
|
||||
|
||||
|
||||
register_snippet(FullFeaturedSnippet, viewset=FullFeaturedSnippetViewSet)
|
||||
|
|
Ładowanie…
Reference in New Issue