From 857b1048d53ebf5fc5581c110e85c212b81ca83a Mon Sep 17 00:00:00 2001 From: GappleBee Date: Thu, 24 Oct 2024 19:44:31 +0100 Subject: [PATCH] Fixed #34619 -- Associated FilteredSelectMultiple elements to their label and help text. --- .../admin/static/admin/css/responsive.css | 3 +- django/contrib/admin/static/admin/css/rtl.css | 8 +- .../admin/static/admin/css/widgets.css | 39 ++++--- .../admin/static/admin/js/SelectFilter2.js | 106 +++++++++++------- js_tests/admin/SelectFilter2.test.js | 24 +++- tests/admin_inlines/tests.py | 38 ++++--- .../test_related_object_lookups.py | 4 +- tests/admin_views/tests.py | 4 +- tests/admin_widgets/tests.py | 50 +++++---- 9 files changed, 168 insertions(+), 108 deletions(-) diff --git a/django/contrib/admin/static/admin/css/responsive.css b/django/contrib/admin/static/admin/css/responsive.css index 932e824c1c..7d296b150f 100644 --- a/django/contrib/admin/static/admin/css/responsive.css +++ b/django/contrib/admin/static/admin/css/responsive.css @@ -299,7 +299,7 @@ input[type="submit"], button { background-position: 0 -80px; } - a.selector-chooseall, a.selector-clearall { + .selector-chooseall, .selector-clearall { align-self: center; } @@ -649,6 +649,7 @@ input[type="submit"], button { .related-widget-wrapper .selector { order: 1; + flex: 1 0 auto; } .related-widget-wrapper > a { diff --git a/django/contrib/admin/static/admin/css/rtl.css b/django/contrib/admin/static/admin/css/rtl.css index b8f60e0a34..5b55b63013 100644 --- a/django/contrib/admin/static/admin/css/rtl.css +++ b/django/contrib/admin/static/admin/css/rtl.css @@ -235,19 +235,19 @@ fieldset .fieldBox { background-position: 0 -112px; } -a.selector-chooseall { +.selector-chooseall { background: url(../img/selector-icons.svg) right -128px no-repeat; } -a.active.selector-chooseall:focus, a.active.selector-chooseall:hover { +.active.selector-chooseall:focus, .active.selector-chooseall:hover { background-position: 100% -144px; } -a.selector-clearall { +.selector-clearall { background: url(../img/selector-icons.svg) 0 -160px no-repeat; } -a.active.selector-clearall:focus, a.active.selector-clearall:hover { +.active.selector-clearall:focus, .active.selector-clearall:hover { background-position: 0 -176px; } diff --git a/django/contrib/admin/static/admin/css/widgets.css b/django/contrib/admin/static/admin/css/widgets.css index cc64811a2b..c8bf90b3b2 100644 --- a/django/contrib/admin/static/admin/css/widgets.css +++ b/django/contrib/admin/static/admin/css/widgets.css @@ -2,7 +2,7 @@ .selector { display: flex; - flex-grow: 1; + flex: 1; gap: 0 10px; } @@ -14,17 +14,20 @@ } .selector-available, .selector-chosen { - text-align: center; display: flex; flex-direction: column; flex: 1 1; } -.selector-available h2, .selector-chosen h2 { +.selector-available-title, .selector-chosen-title { border: 1px solid var(--border-color); border-radius: 4px 4px 0 0; } +.selector .helptext { + font-size: 0.6875rem; +} + .selector-chosen .list-footer-display { border: 1px solid var(--border-color); border-top: none; @@ -40,14 +43,20 @@ color: var(--breadcrumbs-fg); } -.selector-chosen h2 { +.selector-chosen-title { background: var(--secondary); color: var(--header-link-color); + padding: 8px; +} + +.selector-chosen-title label { + color: var(--header-link-color); } -.selector .selector-available h2 { +.selector-available-title { background: var(--darkened-bg); color: var(--body-quiet-color); + padding: 8px; } .selector .selector-filter { @@ -121,6 +130,7 @@ overflow: hidden; cursor: default; opacity: 0.55; + border: none; } .active.selector-add, .active.selector-remove { @@ -147,7 +157,7 @@ background-position: 0 -80px; } -a.selector-chooseall, a.selector-clearall { +.selector-chooseall, .selector-clearall { display: inline-block; height: 16px; text-align: left; @@ -158,38 +168,39 @@ a.selector-chooseall, a.selector-clearall { color: var(--body-quiet-color); text-decoration: none; opacity: 0.55; + border: none; } -a.active.selector-chooseall:focus, a.active.selector-clearall:focus, -a.active.selector-chooseall:hover, a.active.selector-clearall:hover { +.active.selector-chooseall:focus, .active.selector-clearall:focus, +.active.selector-chooseall:hover, .active.selector-clearall:hover { color: var(--link-fg); } -a.active.selector-chooseall, a.active.selector-clearall { +.active.selector-chooseall, .active.selector-clearall { opacity: 1; } -a.active.selector-chooseall:hover, a.active.selector-clearall:hover { +.active.selector-chooseall:hover, .active.selector-clearall:hover { cursor: pointer; } -a.selector-chooseall { +.selector-chooseall { padding: 0 18px 0 0; background: url(../img/selector-icons.svg) right -160px no-repeat; cursor: default; } -a.active.selector-chooseall:focus, a.active.selector-chooseall:hover { +.active.selector-chooseall:focus, .active.selector-chooseall:hover { background-position: 100% -176px; } -a.selector-clearall { +.selector-clearall { padding: 0 0 0 18px; background: url(../img/selector-icons.svg) 0 -128px no-repeat; cursor: default; } -a.active.selector-clearall:focus, a.active.selector-clearall:hover { +.active.selector-clearall:focus, .active.selector-clearall:hover { background-position: 0 -144px; } diff --git a/django/contrib/admin/static/admin/js/SelectFilter2.js b/django/contrib/admin/static/admin/js/SelectFilter2.js index 133d809d52..7f0cfef8c9 100644 --- a/django/contrib/admin/static/admin/js/SelectFilter2.js +++ b/django/contrib/admin/static/admin/js/SelectFilter2.js @@ -15,6 +15,7 @@ Requires core.js and SelectBox.js. const from_box = document.getElementById(field_id); from_box.id += '_from'; // change its ID from_box.className = 'filtered'; + from_box.setAttribute('aria-labelledby', field_id + '_from_title'); for (const p of from_box.parentNode.getElementsByTagName('p')) { if (p.classList.contains("info")) { @@ -38,18 +39,15 @@ Requires core.js and SelectBox.js. //
const selector_available = quickElement('div', selector_div); selector_available.className = 'selector-available'; - const title_available = quickElement('h2', selector_available, interpolate(gettext('Available %s') + ' ', [field_name])); + const selector_available_title = quickElement('div', selector_available); + selector_available_title.id = field_id + '_from_title'; + selector_available_title.className = 'selector-available-title'; + quickElement('label', selector_available_title, interpolate(gettext('Available %s') + ' ', [field_name]), 'for', field_id + '_from'); quickElement( - 'span', title_available, '', - 'class', 'help help-tooltip help-icon', - 'title', interpolate( - gettext( - 'This is the list of available %s. You may choose some by ' + - 'selecting them in the box below and then clicking the ' + - '"Choose" arrow between the two boxes.' - ), - [field_name] - ) + 'p', + selector_available_title, + interpolate(gettext('Choose %s by selecting them and then select the "Choose" arrow button.'), [field_name]), + 'class', 'helptext' ); const filter_p = quickElement('p', selector_available, '', 'id', field_id + '_filter'); @@ -60,7 +58,7 @@ Requires core.js and SelectBox.js. quickElement( 'span', search_filter_label, '', 'class', 'help-tooltip search-label-icon', - 'title', interpolate(gettext("Type into this box to filter down the list of available %s."), [field_name]) + 'aria-label', interpolate(gettext("Type into this box to filter down the list of available %s."), [field_name]) ); filter_p.appendChild(document.createTextNode(' ')); @@ -69,32 +67,44 @@ Requires core.js and SelectBox.js. filter_input.id = field_id + '_input'; selector_available.appendChild(from_box); - const choose_all = quickElement('a', selector_available, gettext('Choose all'), 'title', interpolate(gettext('Click to choose all %s at once.'), [field_name]), 'href', '#', 'id', field_id + '_add_all_link'); - choose_all.className = 'selector-chooseall'; + const choose_all = quickElement( + 'button', + selector_available, + interpolate(gettext('Choose all %s'), [field_name]), + 'id', field_id + '_add_all', + 'class', 'selector-chooseall' + ); //