From 81ffd4ad020b333794ca902944e40c8b09a4bcf0 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Fri, 3 Jan 2025 12:43:04 -0500 Subject: [PATCH 1/3] Fix poor styling of textareas inside the Django admin app. The Django admin app's default stylesheet includes a blanket rule that limits the height of all textarea elements, if the browser window is less than 1024 pixels wide. This interferes with TinyMCE's use of a textarea element in the "Source Code" dialog. In that dialog, the textarea (.tox-textarea) is placed inside a div wrapper (.tox-textarea-wrap); due to TinyMCE's stylesheet, the div has a visible border while the textarea itself has none. So the overall effect is that if you click the "Source Code" button, a large dialog box appears (covering most of the browser window), but only the top 120 pixels of that box are usable. This is visually confusing and makes poor use of screen space. Fix this by adding a small additional stylesheet to the AdminTinyMCE widget. --- tinymce/static/django_tinymce/admin_tinymce.css | 15 +++++++++++++++ tinymce/widgets.py | 8 +++++++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 tinymce/static/django_tinymce/admin_tinymce.css diff --git a/tinymce/static/django_tinymce/admin_tinymce.css b/tinymce/static/django_tinymce/admin_tinymce.css new file mode 100644 index 00000000..49f367d7 --- /dev/null +++ b/tinymce/static/django_tinymce/admin_tinymce.css @@ -0,0 +1,15 @@ +/* +Workaround for the following rule defined in +django/contrib/admin/static/admin/css/responsive.css (which is +unsuitable for textareas used by TinyMCE internally): + +@media (max-width: 1024px) { + textarea { + max-width: 100%; + max-height: 120px; + } +} +*/ +.tox .tox-textarea-wrap .tox-textarea { + max-height: none; +} diff --git a/tinymce/widgets.py b/tinymce/widgets.py index 65f91e0d..784b9c24 100644 --- a/tinymce/widgets.py +++ b/tinymce/widgets.py @@ -116,7 +116,13 @@ def _media(self): class AdminTinyMCE(TinyMCE, admin_widgets.AdminTextareaWidget): - pass + def _media(self): + css = { + "all": ["django_tinymce/admin_tinymce.css"], + } + return forms.Media(css=css) + super().media + + media = property(_media) def get_language_from_django(): From cb8fddb4c1b564ce4fda5ab410acc489784a9476 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Fri, 3 Jan 2025 13:01:14 -0500 Subject: [PATCH 2/3] Add a "code" button to the example toolbar. This allows testing of TinyMCE's source code editor feature. --- tests/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/settings.py b/tests/settings.py index 976cf166..d93cab20 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -84,7 +84,7 @@ "toolbar": "undo redo | blocks | " "bold italic backcolor | alignleft aligncenter " "alignright alignjustify | bullist numlist outdent indent | " - "removeformat | help", + "removeformat | code | help", } DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" From f36540919eb1ad54e277f3871a12c0b56a86caba Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Fri, 3 Jan 2025 13:03:06 -0500 Subject: [PATCH 3/3] Use AdminTinyMCE in the example app's admin pages. The AdminTinyMCE widget is a slightly more opinionated version of the TinyMCE widget, intended to look and behave slightly better when used inside Django admin pages. Additionally, add an admin interface for editing TestModel objects, to allow testing the functionality of the HTMLField class (in the absence of any custom ModelAdmin class.) --- tests/testapp/admin.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/testapp/admin.py b/tests/testapp/admin.py index 93ec06e8..472cb4f3 100644 --- a/tests/testapp/admin.py +++ b/tests/testapp/admin.py @@ -3,8 +3,8 @@ from django.contrib.flatpages.models import FlatPage from django.urls import reverse -from tests.testapp.models import TestInline, TestPage -from tinymce.widgets import TinyMCE +from tests.testapp.models import TestInline, TestModel, TestPage +from tinymce.widgets import AdminTinyMCE class TinyMCETestInlineAdmin(admin.StackedInline): @@ -14,7 +14,7 @@ class TinyMCETestInlineAdmin(admin.StackedInline): def formfield_for_dbfield(self, db_field, **kwargs): if db_field.name in ("content1", "content2"): return db_field.formfield( - widget=TinyMCE( + widget=AdminTinyMCE( attrs={"cols": 80, "rows": 30}, mce_attrs={"external_link_list_url": reverse("tinymce-linklist")}, ) @@ -26,7 +26,7 @@ class TinyMCEFlatPageAdmin(FlatPageAdmin): def formfield_for_dbfield(self, db_field, **kwargs): if db_field.name == "content": return db_field.formfield( - widget=TinyMCE( + widget=AdminTinyMCE( attrs={"cols": 80, "rows": 30}, mce_attrs={"external_link_list_url": reverse("tinymce-linklist")}, ) @@ -40,7 +40,7 @@ class TinyMCETestPageAdmin(admin.ModelAdmin): def formfield_for_dbfield(self, db_field, **kwargs): if db_field.name in ("content1", "content2"): return db_field.formfield( - widget=TinyMCE( + widget=AdminTinyMCE( attrs={"cols": 80, "rows": 30}, mce_attrs={"external_link_list_url": reverse("tinymce-linklist")}, ) @@ -51,3 +51,4 @@ def formfield_for_dbfield(self, db_field, **kwargs): admin.site.unregister(FlatPage) admin.site.register(FlatPage, TinyMCEFlatPageAdmin) admin.site.register(TestPage, TinyMCETestPageAdmin) +admin.site.register(TestModel)