1
0
mirror of https://github.com/django/django.git synced 2024-12-22 00:55:44 +00:00

Fixed #34619 -- Associated FilteredSelectMultiple elements to their label and help text.

This commit is contained in:
GappleBee 2024-10-24 19:44:31 +01:00 committed by Sarah Boyce
parent f60d5e46e1
commit 857b1048d5
9 changed files with 168 additions and 108 deletions

View File

@ -299,7 +299,7 @@ input[type="submit"], button {
background-position: 0 -80px; background-position: 0 -80px;
} }
a.selector-chooseall, a.selector-clearall { .selector-chooseall, .selector-clearall {
align-self: center; align-self: center;
} }
@ -649,6 +649,7 @@ input[type="submit"], button {
.related-widget-wrapper .selector { .related-widget-wrapper .selector {
order: 1; order: 1;
flex: 1 0 auto;
} }
.related-widget-wrapper > a { .related-widget-wrapper > a {

View File

@ -235,19 +235,19 @@ fieldset .fieldBox {
background-position: 0 -112px; background-position: 0 -112px;
} }
a.selector-chooseall { .selector-chooseall {
background: url(../img/selector-icons.svg) right -128px no-repeat; 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; background-position: 100% -144px;
} }
a.selector-clearall { .selector-clearall {
background: url(../img/selector-icons.svg) 0 -160px no-repeat; 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; background-position: 0 -176px;
} }

View File

@ -2,7 +2,7 @@
.selector { .selector {
display: flex; display: flex;
flex-grow: 1; flex: 1;
gap: 0 10px; gap: 0 10px;
} }
@ -14,17 +14,20 @@
} }
.selector-available, .selector-chosen { .selector-available, .selector-chosen {
text-align: center;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1 1; flex: 1 1;
} }
.selector-available h2, .selector-chosen h2 { .selector-available-title, .selector-chosen-title {
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 4px 4px 0 0; border-radius: 4px 4px 0 0;
} }
.selector .helptext {
font-size: 0.6875rem;
}
.selector-chosen .list-footer-display { .selector-chosen .list-footer-display {
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-top: none; border-top: none;
@ -40,14 +43,20 @@
color: var(--breadcrumbs-fg); color: var(--breadcrumbs-fg);
} }
.selector-chosen h2 { .selector-chosen-title {
background: var(--secondary); background: var(--secondary);
color: var(--header-link-color); 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); background: var(--darkened-bg);
color: var(--body-quiet-color); color: var(--body-quiet-color);
padding: 8px;
} }
.selector .selector-filter { .selector .selector-filter {
@ -121,6 +130,7 @@
overflow: hidden; overflow: hidden;
cursor: default; cursor: default;
opacity: 0.55; opacity: 0.55;
border: none;
} }
.active.selector-add, .active.selector-remove { .active.selector-add, .active.selector-remove {
@ -147,7 +157,7 @@
background-position: 0 -80px; background-position: 0 -80px;
} }
a.selector-chooseall, a.selector-clearall { .selector-chooseall, .selector-clearall {
display: inline-block; display: inline-block;
height: 16px; height: 16px;
text-align: left; text-align: left;
@ -158,38 +168,39 @@ a.selector-chooseall, a.selector-clearall {
color: var(--body-quiet-color); color: var(--body-quiet-color);
text-decoration: none; text-decoration: none;
opacity: 0.55; opacity: 0.55;
border: none;
} }
a.active.selector-chooseall:focus, a.active.selector-clearall:focus, .active.selector-chooseall:focus, .active.selector-clearall:focus,
a.active.selector-chooseall:hover, a.active.selector-clearall:hover { .active.selector-chooseall:hover, .active.selector-clearall:hover {
color: var(--link-fg); color: var(--link-fg);
} }
a.active.selector-chooseall, a.active.selector-clearall { .active.selector-chooseall, .active.selector-clearall {
opacity: 1; opacity: 1;
} }
a.active.selector-chooseall:hover, a.active.selector-clearall:hover { .active.selector-chooseall:hover, .active.selector-clearall:hover {
cursor: pointer; cursor: pointer;
} }
a.selector-chooseall { .selector-chooseall {
padding: 0 18px 0 0; padding: 0 18px 0 0;
background: url(../img/selector-icons.svg) right -160px no-repeat; background: url(../img/selector-icons.svg) right -160px no-repeat;
cursor: default; cursor: default;
} }
a.active.selector-chooseall:focus, a.active.selector-chooseall:hover { .active.selector-chooseall:focus, .active.selector-chooseall:hover {
background-position: 100% -176px; background-position: 100% -176px;
} }
a.selector-clearall { .selector-clearall {
padding: 0 0 0 18px; padding: 0 0 0 18px;
background: url(../img/selector-icons.svg) 0 -128px no-repeat; background: url(../img/selector-icons.svg) 0 -128px no-repeat;
cursor: default; cursor: default;
} }
a.active.selector-clearall:focus, a.active.selector-clearall:hover { .active.selector-clearall:focus, .active.selector-clearall:hover {
background-position: 0 -144px; background-position: 0 -144px;
} }

View File

@ -15,6 +15,7 @@ Requires core.js and SelectBox.js.
const from_box = document.getElementById(field_id); const from_box = document.getElementById(field_id);
from_box.id += '_from'; // change its ID from_box.id += '_from'; // change its ID
from_box.className = 'filtered'; from_box.className = 'filtered';
from_box.setAttribute('aria-labelledby', field_id + '_from_title');
for (const p of from_box.parentNode.getElementsByTagName('p')) { for (const p of from_box.parentNode.getElementsByTagName('p')) {
if (p.classList.contains("info")) { if (p.classList.contains("info")) {
@ -38,18 +39,15 @@ Requires core.js and SelectBox.js.
// <div class="selector-available"> // <div class="selector-available">
const selector_available = quickElement('div', selector_div); const selector_available = quickElement('div', selector_div);
selector_available.className = 'selector-available'; 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( quickElement(
'span', title_available, '', 'p',
'class', 'help help-tooltip help-icon', selector_available_title,
'title', interpolate( interpolate(gettext('Choose %s by selecting them and then select the "Choose" arrow button.'), [field_name]),
gettext( 'class', 'helptext'
'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]
)
); );
const filter_p = quickElement('p', selector_available, '', 'id', field_id + '_filter'); const filter_p = quickElement('p', selector_available, '', 'id', field_id + '_filter');
@ -60,7 +58,7 @@ Requires core.js and SelectBox.js.
quickElement( quickElement(
'span', search_filter_label, '', 'span', search_filter_label, '',
'class', 'help-tooltip search-label-icon', '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(' ')); filter_p.appendChild(document.createTextNode(' '));
@ -69,32 +67,44 @@ Requires core.js and SelectBox.js.
filter_input.id = field_id + '_input'; filter_input.id = field_id + '_input';
selector_available.appendChild(from_box); 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'); const choose_all = quickElement(
choose_all.className = 'selector-chooseall'; 'button',
selector_available,
interpolate(gettext('Choose all %s'), [field_name]),
'id', field_id + '_add_all',
'class', 'selector-chooseall'
);
// <ul class="selector-chooser"> // <ul class="selector-chooser">
const selector_chooser = quickElement('ul', selector_div); const selector_chooser = quickElement('ul', selector_div);
selector_chooser.className = 'selector-chooser'; selector_chooser.className = 'selector-chooser';
const add_link = quickElement('a', quickElement('li', selector_chooser), gettext('Choose'), 'title', gettext('Choose'), 'href', '#', 'id', field_id + '_add_link'); const add_button = quickElement(
add_link.className = 'selector-add'; 'button',
const remove_link = quickElement('a', quickElement('li', selector_chooser), gettext('Remove'), 'title', gettext('Remove'), 'href', '#', 'id', field_id + '_remove_link'); quickElement('li', selector_chooser),
remove_link.className = 'selector-remove'; interpolate(gettext('Choose selected %s'), [field_name]),
'id', field_id + '_add',
'class', 'selector-add'
);
const remove_button = quickElement(
'button',
quickElement('li', selector_chooser),
interpolate(gettext('Remove selected chosen %s'), [field_name]),
'id', field_id + '_remove',
'class', 'selector-remove'
);
// <div class="selector-chosen"> // <div class="selector-chosen">
const selector_chosen = quickElement('div', selector_div, '', 'id', field_id + '_selector_chosen'); const selector_chosen = quickElement('div', selector_div, '', 'id', field_id + '_selector_chosen');
selector_chosen.className = 'selector-chosen'; selector_chosen.className = 'selector-chosen';
const title_chosen = quickElement('h2', selector_chosen, interpolate(gettext('Chosen %s') + ' ', [field_name])); const selector_chosen_title = quickElement('div', selector_chosen);
selector_chosen_title.className = 'selector-chosen-title';
selector_chosen_title.id = field_id + '_to_title';
quickElement('label', selector_chosen_title, interpolate(gettext('Chosen %s') + ' ', [field_name]), 'for', field_id + '_to');
quickElement( quickElement(
'span', title_chosen, '', 'p',
'class', 'help help-tooltip help-icon', selector_chosen_title,
'title', interpolate( interpolate(gettext('Remove %s by selecting them and then select the "Remove" arrow button.'), [field_name]),
gettext( 'class', 'helptext'
'This is the list of chosen %s. You may remove some by ' +
'selecting them in the box below and then clicking the ' +
'"Remove" arrow between the two boxes.'
),
[field_name]
)
); );
const filter_selected_p = quickElement('p', selector_chosen, '', 'id', field_id + '_filter_selected'); const filter_selected_p = quickElement('p', selector_chosen, '', 'id', field_id + '_filter_selected');
@ -105,7 +115,7 @@ Requires core.js and SelectBox.js.
quickElement( quickElement(
'span', search_filter_selected_label, '', 'span', search_filter_selected_label, '',
'class', 'help-tooltip search-label-icon', 'class', 'help-tooltip search-label-icon',
'title', interpolate(gettext("Type into this box to filter down the list of selected %s."), [field_name]) 'aria-label', interpolate(gettext("Type into this box to filter down the list of selected %s."), [field_name])
); );
filter_selected_p.appendChild(document.createTextNode(' ')); filter_selected_p.appendChild(document.createTextNode(' '));
@ -113,15 +123,27 @@ Requires core.js and SelectBox.js.
const filter_selected_input = quickElement('input', filter_selected_p, '', 'type', 'text', 'placeholder', gettext("Filter")); const filter_selected_input = quickElement('input', filter_selected_p, '', 'type', 'text', 'placeholder', gettext("Filter"));
filter_selected_input.id = field_id + '_selected_input'; filter_selected_input.id = field_id + '_selected_input';
const to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', '', 'size', from_box.size, 'name', from_box.name); quickElement(
to_box.className = 'filtered'; 'select',
selector_chosen,
'',
'id', field_id + '_to',
'multiple', '',
'size', from_box.size,
'name', from_box.name,
'aria-labelledby', field_id + '_to_title',
'class', 'filtered'
);
const warning_footer = quickElement('div', selector_chosen, '', 'class', 'list-footer-display'); const warning_footer = quickElement('div', selector_chosen, '', 'class', 'list-footer-display');
quickElement('span', warning_footer, '', 'id', field_id + '_list-footer-display-text'); quickElement('span', warning_footer, '', 'id', field_id + '_list-footer-display-text');
quickElement('span', warning_footer, ' ' + gettext('(click to clear)'), 'class', 'list-footer-display__clear'); quickElement('span', warning_footer, ' ' + gettext('(click to clear)'), 'class', 'list-footer-display__clear');
const clear_all = quickElement(
const clear_all = quickElement('a', selector_chosen, gettext('Remove all'), 'title', interpolate(gettext('Click to remove all chosen %s at once.'), [field_name]), 'href', '#', 'id', field_id + '_remove_all_link'); 'button',
clear_all.className = 'selector-clearall'; selector_chosen,
interpolate(gettext('Remove all %s'), [field_name]),
'id', field_id + '_remove_all',
'class', 'selector-clearall'
);
from_box.name = from_box.name + '_old'; from_box.name = from_box.name + '_old';
@ -138,10 +160,10 @@ Requires core.js and SelectBox.js.
choose_all.addEventListener('click', function(e) { choose_all.addEventListener('click', function(e) {
move_selection(e, this, SelectBox.move_all, field_id + '_from', field_id + '_to'); move_selection(e, this, SelectBox.move_all, field_id + '_from', field_id + '_to');
}); });
add_link.addEventListener('click', function(e) { add_button.addEventListener('click', function(e) {
move_selection(e, this, SelectBox.move, field_id + '_from', field_id + '_to'); move_selection(e, this, SelectBox.move, field_id + '_from', field_id + '_to');
}); });
remove_link.addEventListener('click', function(e) { remove_button.addEventListener('click', function(e) {
move_selection(e, this, SelectBox.move, field_id + '_to', field_id + '_from'); move_selection(e, this, SelectBox.move, field_id + '_to', field_id + '_from');
}); });
clear_all.addEventListener('click', function(e) { clear_all.addEventListener('click', function(e) {
@ -227,11 +249,11 @@ Requires core.js and SelectBox.js.
const from = document.getElementById(field_id + '_from'); const from = document.getElementById(field_id + '_from');
const to = document.getElementById(field_id + '_to'); const to = document.getElementById(field_id + '_to');
// Active if at least one item is selected // Active if at least one item is selected
document.getElementById(field_id + '_add_link').classList.toggle('active', SelectFilter.any_selected(from)); document.getElementById(field_id + '_add').classList.toggle('active', SelectFilter.any_selected(from));
document.getElementById(field_id + '_remove_link').classList.toggle('active', SelectFilter.any_selected(to)); document.getElementById(field_id + '_remove').classList.toggle('active', SelectFilter.any_selected(to));
// Active if the corresponding box isn't empty // Active if the corresponding box isn't empty
document.getElementById(field_id + '_add_all_link').classList.toggle('active', from.querySelector('option')); document.getElementById(field_id + '_add_all').classList.toggle('active', from.querySelector('option'));
document.getElementById(field_id + '_remove_all_link').classList.toggle('active', to.querySelector('option')); document.getElementById(field_id + '_remove_all').classList.toggle('active', to.querySelector('option'));
SelectFilter.refresh_filtered_warning(field_id); SelectFilter.refresh_filtered_warning(field_id);
}, },
filter_key_press: function(event, field_id, source, target) { filter_key_press: function(event, field_id, source, target) {

View File

@ -12,13 +12,25 @@ QUnit.test('init', function(assert) {
SelectFilter.init('id', 'things', 0); SelectFilter.init('id', 'things', 0);
assert.equal($('#test').children().first().prop("tagName"), "DIV"); assert.equal($('#test').children().first().prop("tagName"), "DIV");
assert.equal($('#test').children().first().attr("class"), "selector"); assert.equal($('#test').children().first().attr("class"), "selector");
assert.equal($('.selector-available h2').text().trim(), "Available things"); assert.equal($('.selector-available label').text().trim(), "Available things");
assert.equal($('.selector-chosen h2').text().trim(), "Chosen things"); assert.equal($('.selector-chosen label').text().trim(), "Chosen things");
assert.equal($('.selector-chosen select')[0].getAttribute('multiple'), ''); assert.equal($('.selector-chosen select')[0].getAttribute('multiple'), '');
assert.equal($('.selector-chooseall').text(), "Choose all"); assert.equal($('.selector-chooseall').text(), "Choose all things");
assert.equal($('.selector-add').text(), "Choose"); assert.equal($('.selector-chooseall').prop("tagName"), "BUTTON");
assert.equal($('.selector-remove').text(), "Remove"); assert.equal($('.selector-add').text(), "Choose selected things");
assert.equal($('.selector-clearall').text(), "Remove all"); assert.equal($('.selector-add').prop("tagName"), "BUTTON");
assert.equal($('.selector-remove').text(), "Remove selected chosen things");
assert.equal($('.selector-remove').prop("tagName"), "BUTTON");
assert.equal($('.selector-clearall').text(), "Remove all things");
assert.equal($('.selector-clearall').prop("tagName"), "BUTTON");
assert.equal($('.selector-available .filtered').attr("aria-labelledby"), "id_from_title");
assert.equal($('.selector-available .selector-available-title label').text(), "Available things ");
assert.equal($('.selector-available .selector-available-title .helptext').text(), 'Choose things by selecting them and then select the "Choose" arrow button.');
assert.equal($('.selector-chosen .filtered').attr("aria-labelledby"), "id_to_title");
assert.equal($('.selector-chosen .selector-chosen-title label').text(), "Chosen things ");
assert.equal($('.selector-chosen .selector-chosen-title .helptext').text(), 'Remove things by selecting them and then select the "Remove" arrow button.');
assert.equal($('.selector-filter label .help-tooltip')[0].getAttribute("aria-label"), "Type into this box to filter down the list of available things.");
assert.equal($('.selector-filter label .help-tooltip')[1].getAttribute("aria-label"), "Type into this box to filter down the list of selected things.");
}); });
QUnit.test('filtering available options', function(assert) { QUnit.test('filtering available options', function(assert) {

View File

@ -2420,31 +2420,43 @@ class SeleniumTests(AdminSeleniumTestCase):
"admin:admin_inlines_courseproxy1_add", "admin:admin_inlines_courseproxy1_add",
"admin:admin_inlines_courseproxy2_add", "admin:admin_inlines_courseproxy2_add",
] ]
css_selector = ".dynamic-class_set#class_set-%s h2" css_available_selector = (
".dynamic-class_set#class_set-%s .selector-available-title"
)
css_chosen_selector = ".dynamic-class_set#class_set-%s .selector-chosen-title"
for url_name in tests: for url_name in tests:
with self.subTest(url=url_name): with self.subTest(url=url_name):
self.selenium.get(self.live_server_url + reverse(url_name)) self.selenium.get(self.live_server_url + reverse(url_name))
# First inline shows the verbose_name. # First inline shows the verbose_name.
available, chosen = self.selenium.find_elements( available = self.selenium.find_element(
By.CSS_SELECTOR, css_selector % 0 By.CSS_SELECTOR, css_available_selector % 0
) )
self.assertEqual(available.text, "AVAILABLE ATTENDANT") chosen = self.selenium.find_element(
self.assertEqual(chosen.text, "CHOSEN ATTENDANT") By.CSS_SELECTOR, css_chosen_selector % 0
)
self.assertIn("Available attendant", available.text)
self.assertIn("Chosen attendant", chosen.text)
# Added inline should also have the correct verbose_name. # Added inline should also have the correct verbose_name.
self.selenium.find_element(By.LINK_TEXT, "Add another Class").click() self.selenium.find_element(By.LINK_TEXT, "Add another Class").click()
available, chosen = self.selenium.find_elements( available = self.selenium.find_element(
By.CSS_SELECTOR, css_selector % 1 By.CSS_SELECTOR, css_available_selector % 1
) )
self.assertEqual(available.text, "AVAILABLE ATTENDANT") chosen = self.selenium.find_element(
self.assertEqual(chosen.text, "CHOSEN ATTENDANT") By.CSS_SELECTOR, css_chosen_selector % 1
)
self.assertIn("Available attendant", available.text)
self.assertIn("Chosen attendant", chosen.text)
# Third inline should also have the correct verbose_name. # Third inline should also have the correct verbose_name.
self.selenium.find_element(By.LINK_TEXT, "Add another Class").click() self.selenium.find_element(By.LINK_TEXT, "Add another Class").click()
available, chosen = self.selenium.find_elements( available = self.selenium.find_element(
By.CSS_SELECTOR, css_selector % 2 By.CSS_SELECTOR, css_available_selector % 2
) )
self.assertEqual(available.text, "AVAILABLE ATTENDANT") chosen = self.selenium.find_element(
self.assertEqual(chosen.text, "CHOSEN ATTENDANT") By.CSS_SELECTOR, css_chosen_selector % 2
)
self.assertIn("Available attendant", available.text)
self.assertIn("Chosen attendant", chosen.text)
def test_tabular_inline_layout(self): def test_tabular_inline_layout(self):
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By

View File

@ -162,7 +162,7 @@ class SeleniumTests(AdminSeleniumTestCase):
# Move the new value to the from box. # Move the new value to the from box.
self.selenium.find_element(By.XPATH, "//*[@id='id_m2m_to']/option").click() self.selenium.find_element(By.XPATH, "//*[@id='id_m2m_to']/option").click()
self.selenium.find_element(By.XPATH, "//*[@id='id_m2m_remove_link']").click() self.selenium.find_element(By.XPATH, "//*[@id='id_m2m_remove']").click()
self.assertHTMLEqual( self.assertHTMLEqual(
m2m_box.get_attribute("innerHTML"), m2m_box.get_attribute("innerHTML"),
@ -172,7 +172,7 @@ class SeleniumTests(AdminSeleniumTestCase):
# Move the new value to the to box. # Move the new value to the to box.
self.selenium.find_element(By.XPATH, "//*[@id='id_m2m_from']/option").click() self.selenium.find_element(By.XPATH, "//*[@id='id_m2m_from']/option").click()
self.selenium.find_element(By.XPATH, "//*[@id='id_m2m_add_link']").click() self.selenium.find_element(By.XPATH, "//*[@id='id_m2m_add']").click()
self.assertHTMLEqual(m2m_box.get_attribute("innerHTML"), "") self.assertHTMLEqual(m2m_box.get_attribute("innerHTML"), "")
self.assertHTMLEqual( self.assertHTMLEqual(

View File

@ -6225,9 +6225,7 @@ class SeleniumTests(AdminSeleniumTestCase):
self.take_screenshot("selectbox-available-perms-some-selected") self.take_screenshot("selectbox-available-perms-some-selected")
# Move permissions to the "Chosen" list, but none is selected yet. # Move permissions to the "Chosen" list, but none is selected yet.
self.selenium.find_element( self.selenium.find_element(By.CSS_SELECTOR, "#id_user_permissions_add").click()
By.CSS_SELECTOR, "#id_user_permissions_add_link"
).click()
self.take_screenshot("selectbox-chosen-perms-none-selected") self.take_screenshot("selectbox-chosen-perms-none-selected")
# Select some permissions from the "Chosen" list. # Select some permissions from the "Chosen" list.

View File

@ -1257,15 +1257,19 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
def assertActiveButtons( def assertActiveButtons(
self, mode, field_name, choose, remove, choose_all=None, remove_all=None self, mode, field_name, choose, remove, choose_all=None, remove_all=None
): ):
choose_link = "#id_%s_add_link" % field_name choose_button = "#id_%s_add" % field_name
choose_all_link = "#id_%s_add_all_link" % field_name choose_all_button = "#id_%s_add_all" % field_name
remove_link = "#id_%s_remove_link" % field_name remove_button = "#id_%s_remove" % field_name
remove_all_link = "#id_%s_remove_all_link" % field_name remove_all_button = "#id_%s_remove_all" % field_name
self.assertEqual(self.has_css_class(choose_link, "active"), choose) self.assertEqual(self.has_css_class(choose_button, "active"), choose)
self.assertEqual(self.has_css_class(remove_link, "active"), remove) self.assertEqual(self.has_css_class(remove_button, "active"), remove)
if mode == "horizontal": if mode == "horizontal":
self.assertEqual(self.has_css_class(choose_all_link, "active"), choose_all) self.assertEqual(
self.assertEqual(self.has_css_class(remove_all_link, "active"), remove_all) self.has_css_class(choose_all_button, "active"), choose_all
)
self.assertEqual(
self.has_css_class(remove_all_button, "active"), remove_all
)
def execute_basic_operations(self, mode, field_name): def execute_basic_operations(self, mode, field_name):
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
@ -1274,10 +1278,10 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
from_box = "#id_%s_from" % field_name from_box = "#id_%s_from" % field_name
to_box = "#id_%s_to" % field_name to_box = "#id_%s_to" % field_name
choose_link = "id_%s_add_link" % field_name choose_button = "id_%s_add" % field_name
choose_all_link = "id_%s_add_all_link" % field_name choose_all_button = "id_%s_add_all" % field_name
remove_link = "id_%s_remove_link" % field_name remove_button = "id_%s_remove" % field_name
remove_all_link = "id_%s_remove_all_link" % field_name remove_all_button = "id_%s_remove_all" % field_name
# Initial positions --------------------------------------------------- # Initial positions ---------------------------------------------------
self.assertSelectOptions( self.assertSelectOptions(
@ -1296,7 +1300,7 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
# Click 'Choose all' -------------------------------------------------- # Click 'Choose all' --------------------------------------------------
if mode == "horizontal": if mode == "horizontal":
self.selenium.find_element(By.ID, choose_all_link).click() self.selenium.find_element(By.ID, choose_all_button).click()
elif mode == "vertical": elif mode == "vertical":
# There 's no 'Choose all' button in vertical mode, so individually # There 's no 'Choose all' button in vertical mode, so individually
# select all options and click 'Choose'. # select all options and click 'Choose'.
@ -1304,7 +1308,7 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
By.CSS_SELECTOR, from_box + " > option" By.CSS_SELECTOR, from_box + " > option"
): ):
option.click() option.click()
self.selenium.find_element(By.ID, choose_link).click() self.selenium.find_element(By.ID, choose_button).click()
self.assertSelectOptions(from_box, []) self.assertSelectOptions(from_box, [])
self.assertSelectOptions( self.assertSelectOptions(
to_box, to_box,
@ -1323,7 +1327,7 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
# Click 'Remove all' -------------------------------------------------- # Click 'Remove all' --------------------------------------------------
if mode == "horizontal": if mode == "horizontal":
self.selenium.find_element(By.ID, remove_all_link).click() self.selenium.find_element(By.ID, remove_all_button).click()
elif mode == "vertical": elif mode == "vertical":
# There 's no 'Remove all' button in vertical mode, so individually # There 's no 'Remove all' button in vertical mode, so individually
# select all options and click 'Remove'. # select all options and click 'Remove'.
@ -1331,7 +1335,7 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
By.CSS_SELECTOR, to_box + " > option" By.CSS_SELECTOR, to_box + " > option"
): ):
option.click() option.click()
self.selenium.find_element(By.ID, remove_link).click() self.selenium.find_element(By.ID, remove_button).click()
self.assertSelectOptions( self.assertSelectOptions(
from_box, from_box,
[ [
@ -1364,7 +1368,7 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
self.select_option(from_box, str(self.bob.id)) self.select_option(from_box, str(self.bob.id))
self.select_option(from_box, str(self.john.id)) self.select_option(from_box, str(self.john.id))
self.assertActiveButtons(mode, field_name, True, False, True, False) self.assertActiveButtons(mode, field_name, True, False, True, False)
self.selenium.find_element(By.ID, choose_link).click() self.selenium.find_element(By.ID, choose_button).click()
self.assertActiveButtons(mode, field_name, False, False, True, True) self.assertActiveButtons(mode, field_name, False, False, True, True)
self.assertSelectOptions( self.assertSelectOptions(
@ -1399,7 +1403,7 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
self.select_option(to_box, str(self.lisa.id)) self.select_option(to_box, str(self.lisa.id))
self.select_option(to_box, str(self.bob.id)) self.select_option(to_box, str(self.bob.id))
self.assertActiveButtons(mode, field_name, False, True, True, True) self.assertActiveButtons(mode, field_name, False, True, True, True)
self.selenium.find_element(By.ID, remove_link).click() self.selenium.find_element(By.ID, remove_button).click()
self.assertActiveButtons(mode, field_name, False, False, True, True) self.assertActiveButtons(mode, field_name, False, False, True, True)
self.assertSelectOptions( self.assertSelectOptions(
@ -1418,7 +1422,7 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
# Choose some more options -------------------------------------------- # Choose some more options --------------------------------------------
self.select_option(from_box, str(self.arthur.id)) self.select_option(from_box, str(self.arthur.id))
self.select_option(from_box, str(self.cliff.id)) self.select_option(from_box, str(self.cliff.id))
self.selenium.find_element(By.ID, choose_link).click() self.selenium.find_element(By.ID, choose_button).click()
self.assertSelectOptions( self.assertSelectOptions(
from_box, from_box,
@ -1445,7 +1449,7 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
# Confirm they're selected after clicking inactive buttons: ticket #26575 # Confirm they're selected after clicking inactive buttons: ticket #26575
self.assertSelectedOptions(from_box, [str(self.peter.id), str(self.lisa.id)]) self.assertSelectedOptions(from_box, [str(self.peter.id), str(self.lisa.id)])
self.selenium.find_element(By.ID, remove_link).click() self.selenium.find_element(By.ID, remove_button).click()
self.assertSelectedOptions(from_box, [str(self.peter.id), str(self.lisa.id)]) self.assertSelectedOptions(from_box, [str(self.peter.id), str(self.lisa.id)])
# Unselect the options ------------------------------------------------ # Unselect the options ------------------------------------------------
@ -1458,7 +1462,7 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
# Confirm they're selected after clicking inactive buttons: ticket #26575 # Confirm they're selected after clicking inactive buttons: ticket #26575
self.assertSelectedOptions(to_box, [str(self.jason.id), str(self.john.id)]) self.assertSelectedOptions(to_box, [str(self.jason.id), str(self.john.id)])
self.selenium.find_element(By.ID, choose_link).click() self.selenium.find_element(By.ID, choose_button).click()
self.assertSelectedOptions(to_box, [str(self.jason.id), str(self.john.id)]) self.assertSelectedOptions(to_box, [str(self.jason.id), str(self.john.id)])
# Unselect the options ------------------------------------------------ # Unselect the options ------------------------------------------------
@ -1520,8 +1524,8 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
for field_name in ["students", "alumni"]: for field_name in ["students", "alumni"]:
from_box = "#id_%s_from" % field_name from_box = "#id_%s_from" % field_name
to_box = "#id_%s_to" % field_name to_box = "#id_%s_to" % field_name
choose_link = "id_%s_add_link" % field_name choose_link = "id_%s_add" % field_name
remove_link = "id_%s_remove_link" % field_name remove_link = "id_%s_remove" % field_name
input = self.selenium.find_element(By.ID, "id_%s_input" % field_name) input = self.selenium.find_element(By.ID, "id_%s_input" % field_name)
# Initial values. # Initial values.
self.assertSelectOptions( self.assertSelectOptions(