summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorantoliny0919 <antoliny0919@gmail.com>2025-07-24 19:15:32 +0900
committerSarah Boyce <42296566+sarahboyce@users.noreply.github.com>2025-07-30 11:09:45 +0200
commit94c2f3b99364bcf2d6d6b4afa8e995bd9d61ed0a (patch)
tree9d548e65a5e65d2f9ef72045d15bda3bf6d4bfbe
parentf96c8f07e45f56f1f72125aca7f33eb3092f51b3 (diff)
Fixed #36055 -- Prevented overlap of object-tools buttons and page header in the admin.
-rw-r--r--django/contrib/admin/static/admin/css/base.css20
-rw-r--r--django/contrib/admin/static/admin/css/responsive.css14
-rw-r--r--django/contrib/admin/static/admin/css/responsive_rtl.css14
-rw-r--r--django/contrib/admin/static/admin/css/rtl.css7
-rw-r--r--docs/intro/_images/admin04t.pngbin26964 -> 15538 bytes
-rw-r--r--docs/intro/_images/admin05t.pngbin16451 -> 35611 bytes
-rw-r--r--docs/intro/_images/admin12t.pngbin32958 -> 24331 bytes
-rw-r--r--docs/intro/_images/admin13t.pngbin52256 -> 41136 bytes
-rw-r--r--docs/ref/contrib/admin/_images/actions-as-modeladmin-methods.pngbin112201 -> 89522 bytes
-rw-r--r--docs/ref/contrib/admin/_images/adding-actions-to-the-modeladmin.pngbin96045 -> 75813 bytes
-rw-r--r--docs/ref/contrib/admin/_images/list_filter.pngbin76459 -> 55201 bytes
-rw-r--r--tests/admin_views/models.py6
-rw-r--r--tests/admin_views/tests.py32
13 files changed, 60 insertions, 33 deletions
diff --git a/django/contrib/admin/static/admin/css/base.css b/django/contrib/admin/static/admin/css/base.css
index e2be6a78fe..082dafc141 100644
--- a/django/contrib/admin/static/admin/css/base.css
+++ b/django/contrib/admin/static/admin/css/base.css
@@ -808,19 +808,19 @@ a.deletelink:focus, a.deletelink:hover {
/* OBJECT TOOLS */
.object-tools {
- font-size: 0.625rem;
- font-weight: bold;
- padding-left: 0;
- float: right;
- position: relative;
- margin-top: -48px;
+ padding: 0;
+ overflow: hidden;
+ text-align: right;
+ margin: 0 0 15px;
}
.object-tools li {
- display: block;
- float: left;
- margin-left: 5px;
- height: 1rem;
+ display: inline-block;
+ height: auto;
+}
+
+.object-tools li + li {
+ margin-left: 15px;
}
.object-tools a {
diff --git a/django/contrib/admin/static/admin/css/responsive.css b/django/contrib/admin/static/admin/css/responsive.css
index 7a2c321ceb..9aa895316c 100644
--- a/django/contrib/admin/static/admin/css/responsive.css
+++ b/django/contrib/admin/static/admin/css/responsive.css
@@ -442,19 +442,7 @@ input[type="submit"], button {
}
.object-tools {
- float: none;
- margin: 0 0 15px;
- padding: 0;
- overflow: hidden;
- }
-
- .object-tools li {
- height: auto;
- margin-left: 0;
- }
-
- .object-tools li + li {
- margin-left: 15px;
+ text-align: left;
}
/* Forms */
diff --git a/django/contrib/admin/static/admin/css/responsive_rtl.css b/django/contrib/admin/static/admin/css/responsive_rtl.css
index acad9e8b6c..b336bbfbe9 100644
--- a/django/contrib/admin/static/admin/css/responsive_rtl.css
+++ b/django/contrib/admin/static/admin/css/responsive_rtl.css
@@ -34,15 +34,6 @@
background-position: calc(100% - 8px) 9px;
}
- [dir="rtl"] .object-tools li {
- float: right;
- }
-
- [dir="rtl"] .object-tools li + li {
- margin-left: 0;
- margin-right: 15px;
- }
-
[dir="rtl"] .dashboard .module table td a {
padding-left: 0;
padding-right: 16px;
@@ -72,6 +63,11 @@
margin-left: 0;
margin-right: 0;
}
+
+ [dir="rtl"] .object-tools {
+ text-align: right;
+ }
+
[dir="rtl"] .aligned .vCheckboxLabel {
padding: 1px 5px 0 0;
}
diff --git a/django/contrib/admin/static/admin/css/rtl.css b/django/contrib/admin/static/admin/css/rtl.css
index b824d7d0db..f484d55275 100644
--- a/django/contrib/admin/static/admin/css/rtl.css
+++ b/django/contrib/admin/static/admin/css/rtl.css
@@ -26,7 +26,12 @@ th {
}
.object-tools {
- float: left;
+ text-align: left;
+}
+
+.object-tools li + li {
+ margin-right: 15px;
+ margin-left: 0;
}
thead th:first-child,
diff --git a/docs/intro/_images/admin04t.png b/docs/intro/_images/admin04t.png
index e8f5ea8a6f..ef5e12b93c 100644
--- a/docs/intro/_images/admin04t.png
+++ b/docs/intro/_images/admin04t.png
Binary files differ
diff --git a/docs/intro/_images/admin05t.png b/docs/intro/_images/admin05t.png
index 427a26ba18..0007fae910 100644
--- a/docs/intro/_images/admin05t.png
+++ b/docs/intro/_images/admin05t.png
Binary files differ
diff --git a/docs/intro/_images/admin12t.png b/docs/intro/_images/admin12t.png
index c4a4192855..b79465a4ac 100644
--- a/docs/intro/_images/admin12t.png
+++ b/docs/intro/_images/admin12t.png
Binary files differ
diff --git a/docs/intro/_images/admin13t.png b/docs/intro/_images/admin13t.png
index ae39044608..1567d32393 100644
--- a/docs/intro/_images/admin13t.png
+++ b/docs/intro/_images/admin13t.png
Binary files differ
diff --git a/docs/ref/contrib/admin/_images/actions-as-modeladmin-methods.png b/docs/ref/contrib/admin/_images/actions-as-modeladmin-methods.png
index c8c67aa566..997a0e16c8 100644
--- a/docs/ref/contrib/admin/_images/actions-as-modeladmin-methods.png
+++ b/docs/ref/contrib/admin/_images/actions-as-modeladmin-methods.png
Binary files differ
diff --git a/docs/ref/contrib/admin/_images/adding-actions-to-the-modeladmin.png b/docs/ref/contrib/admin/_images/adding-actions-to-the-modeladmin.png
index e08edee6cf..1d539fe446 100644
--- a/docs/ref/contrib/admin/_images/adding-actions-to-the-modeladmin.png
+++ b/docs/ref/contrib/admin/_images/adding-actions-to-the-modeladmin.png
Binary files differ
diff --git a/docs/ref/contrib/admin/_images/list_filter.png b/docs/ref/contrib/admin/_images/list_filter.png
index 777034516d..bdb58f9f38 100644
--- a/docs/ref/contrib/admin/_images/list_filter.png
+++ b/docs/ref/contrib/admin/_images/list_filter.png
Binary files differ
diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py
index a20130bb02..8f102dece3 100644
--- a/tests/admin_views/models.py
+++ b/tests/admin_views/models.py
@@ -973,6 +973,12 @@ class Restaurant(models.Model):
city = models.ForeignKey(City, models.CASCADE)
name = models.CharField(max_length=100)
+ class Meta:
+ verbose_name = (
+ "very very very very very very very very very "
+ "loooooooooooooooooooooooooooooooooooooooooong name"
+ )
+
def get_absolute_url(self):
return "/dummy/%s/" % self.pk
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index 00b5d5ab89..65241becc0 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -126,6 +126,7 @@ from .models import (
Song,
State,
Story,
+ Subscriber,
SuperSecretHideout,
SuperVillain,
Telegram,
@@ -6955,6 +6956,37 @@ class SeleniumTests(AdminSeleniumTestCase):
with self.wait_page_loaded():
save_button.click()
+ @screenshot_cases(["desktop_size", "mobile_size", "rtl", "dark", "high_contrast"])
+ def test_long_header_with_object_tools_layout(self):
+ from selenium.webdriver.common.by import By
+
+ self.admin_login(
+ username="super", password="secret", login_url=reverse("admin:index")
+ )
+ s = Subscriber.objects.create(name="a " * 40, email="b " * 80)
+ self.selenium.get(
+ self.live_server_url
+ + reverse("admin:admin_views_subscriber_change", args=(s.pk,))
+ )
+ header = self.selenium.find_element(By.CSS_SELECTOR, "div#content h2")
+ self.assertGreater(len(header.text), 100)
+ object_tools = self.selenium.find_elements(
+ By.CSS_SELECTOR, "div#content ul.object-tools li"
+ )
+ self.assertGreater(len(object_tools), 0)
+ self.take_screenshot("change_form")
+
+ self.selenium.get(
+ self.live_server_url + reverse("admin:admin_views_restaurant_changelist")
+ )
+ header = self.selenium.find_element(By.CSS_SELECTOR, "div#content h1")
+ self.assertGreater(len(header.text), 100)
+ object_tools = self.selenium.find_elements(
+ By.CSS_SELECTOR, "div#content ul.object-tools li"
+ )
+ self.assertGreater(len(object_tools), 0)
+ self.take_screenshot("change_list")
+
@override_settings(ROOT_URLCONF="admin_views.urls")
class ReadonlyTest(AdminFieldExtractionMixin, TestCase):