Skip to content

Commit 0e91248

Browse files
Accessibility ergonomy filtering (#11634)
* Added vertical alignment for filter and clear filter logic * Apply clear filter logic * Fix new line at the end of file * Updated unit tests * Removed forgotten console log * Updated selector for integration test
1 parent 9e8111a commit 0e91248

8 files changed

Lines changed: 139 additions & 59 deletions

File tree

dojo/static/dojo/css/dojo.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1278,7 +1278,7 @@ div.custom-search-form {
12781278
}
12791279

12801280
.dojo-filter-set.form-inline .filter-form-control {
1281-
width: auto!important;
1281+
width: 100%!important;
12821282
vertical-align: middle;
12831283
}
12841284

dojo/static/dojo/js/index.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,13 @@ function clear_form(form){
342342
case 'radio':
343343
this.checked = false;
344344
break;
345-
case 'select-multiple':
346-
$(this).val(null).trigger('change');
345+
case 'select-multiple':
346+
// Clear all types of multiple select versions
347+
if ($(this).hasClass('select2-hidden-accessible')) {
348+
$(this).data('select2').$container.find(".select2-selection__choice").remove();
349+
}
350+
$(this).val(null).trigger('change');
351+
break;
347352
}
348353
});
349-
}
354+
}

dojo/templates/dojo/filter_js_snippet.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
$(function () {
1010
$('.similar-filters select[multiple]').not('[data-tagulous]').each(function () {
11+
// Removing bootstrap-select class and replacing with select2 to avoid library conflicts
12+
// leading to visually unappealing dropdowns
13+
$(this).removeClass('selectpicker').closest('.bootstrap-select').replaceWith($(this));
1114
$(this).select2(
1215
)
1316
})

dojo/templates/dojo/filter_snippet.html

Lines changed: 118 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -15,54 +15,67 @@
1515
{% for field in form.hidden_fields %}
1616
{{ field }}
1717
{% endfor %}
18-
<div class="filter-form-group">
19-
{% for field in form.visible_fields %}
20-
<div class="filter-form-input">
21-
{{ field.errors }}
22-
<label for="{{ field.auto_id }}" style="display: block;">
23-
{{ field.label }}
24-
{% if field.help_text %}
25-
<i class="fa-solid fa-circle-question has-popover" data-trigger="hover" data-content="{{ field.help_text }}" data-placement="right" data-container="body">
26-
</i>
18+
<div class="container-fluid filter-form-group">
19+
{% for field in form.visible_fields %}
20+
<div class="col-lg-3 col-md-4 col-sm-6 col-12">
21+
<div class="filter-form-input" style="min-width: 0;">
22+
<label for="{{ field.auto_id }}" class="form-label mb-1" style="display: block;">
23+
{{ field.label }}
24+
{% if field.help_text %}
25+
<i class="fa-solid fa-circle-question has-popover"
26+
data-trigger="hover"
27+
data-content="{{ field.help_text }}"
28+
data-placement="right"
29+
data-container="body">
30+
</i>
31+
{% endif %}
32+
</label>
33+
{% with placeholder="placeholder:"|add:field.label %}
34+
{{ field|addcss:"class: form-control filter-form-control"|addcss:placeholder }}
35+
{% endwith %}
36+
</div>
37+
</div>
38+
{% endfor %}
39+
</div>
40+
<div class="container-fluid">
41+
<div class="row mt-3">
42+
<div class="col-12">
43+
<div class="d-flex justify-content-end align-items-center">
44+
{% if submit == 'report' %}
45+
{% query_string_as_hidden %}
46+
<button class="btn btn-secondary" name="_generate" type="submit">
47+
<i class="fa-solid fa-file-lines"></i> Generate Report
48+
</button>
49+
{% else %}
50+
<button id="apply" class="btn btn-secondary me-2">
51+
<i class="fa-solid fa-filter"></i> Apply Filters
52+
</button>
53+
{% if clear_js %}
54+
<a class="btn btn-outline-secondary me-2" href="#{{form_id}}" id="clear_js" role="button">
55+
<i class="fa-solid fa-remove"></i> Clear Filters
56+
</a>
57+
{% else %}
58+
<a class="btn btn-outline-secondary me-2" href="{{ clear_link|default:request.path }}" id="clear" role="button" >
59+
<i class="fa-solid fa-remove"></i> Clear Filters
60+
</a>
61+
{% endif %}
62+
{% if restart_link %}
63+
<a href="{{ restart_link }}" id="restart" class="btn btn-secondary">
64+
<i class="fa-solid fa-remove"></i> Restart
65+
</a>
2766
{% endif %}
28-
</label>
29-
{% with placeholder="placeholder:"|add:field.label %}
30-
{{ field|addcss:"class: form-control filter-form-control"|addcss:placeholder }}
31-
{% endwith %}
67+
{% endif %}
68+
</div>
3269
</div>
33-
{% endfor %}
34-
</div>
35-
{% if submit == 'report' %}
36-
{% query_string_as_hidden %}
37-
<div class="inline-block" style="vertical-align: text-top">
38-
<button class="btn btn-secondary" name="_generate" type="submit">
39-
<i class="fa-solid fa-file-lines"></i> Generate Report
40-
</button>
41-
</div>
42-
{% else %}
43-
<div class="inline-block" style="vertical-align: text-top">
44-
<button id="apply" class="btn btn-sm btn-secondary">
45-
<i class="fa-solid fa-filter"></i> Apply Filters
46-
</button>
47-
&nbsp;
48-
{% if clear_js %}
49-
<a href="#{{form_id}}" id="clear_js" class="clear centered"> [Clear Filters] </a>
50-
{% elif clear_link %}
51-
<a href="{{ clear_link }}" id="clear" class="clear centered"> [Clear Filters] </a>
52-
{% else %}
53-
<a href="{{ request.path }}" id="clear" class="clear centered"> [Clear Filters] </a>
54-
{% endif %}
55-
{% if restart_link %}
56-
<a href="{{ restart_link }}" id="clear" class="clear centered"> [Restart] </a>
57-
{% endif %}
5870
</div>
59-
{% endif %}
71+
</div>
72+
6073
</form>
61-
6274
</div>
6375
<script>
6476
$(document).ready(function() {
65-
$(".filter-set>form").first().submit(function(event) {
77+
const form = $(".filter-set>form");
78+
$(form).first().submit(function(event) {
6679
var formData = $(".filter-set>form").first().serializeArray();
6780
var filteredFormData = formData.filter(function(item) {
6881
// Remove null or empty values
@@ -81,11 +94,70 @@
8194

8295
// Append the new query parameters to the base URL
8396
var newAction = baseUrl + "?" + queryParams.join("&");
84-
85-
// Append the query parameters to the action URL
86-
var newAction = baseUrl + "?" + queryParams.join("&");
8797
window.location.href = newAction;
8898
event.preventDefault();
8999
});
90-
});
91-
</script>
100+
101+
// Clear filter logic below should clear filters without reload and
102+
// have the button disabled if no filters are active
103+
104+
const clearFilterLink = $("#clear, #clear_js");
105+
106+
const hasActiveFilters = () => {
107+
return $(form)
108+
.find(":input:not([type='hidden'])")
109+
.filter(function () {
110+
const value = $(this).val();
111+
112+
if ($(this).is("select[multiple]")) {
113+
// Checking if value is an array and has non-empty elements at the same time
114+
return Array.isArray(value) && value.some(v => v && v.trim() !== "");
115+
} else if ($(this).is(":checkbox, :radio")) {
116+
// Checkboxes and radio buttons
117+
return $(this).prop("checked");
118+
} else if (typeof value === "string") {
119+
// Text inputs, textareas
120+
return value.trim() !== "" && value !== "unknown" && value !== null;
121+
} else {
122+
// Other input types
123+
return value !== null && value !== undefined;
124+
}
125+
}).length > 0;
126+
};
127+
128+
const updateClearFiltersState = () => {
129+
const filtersActive = hasActiveFilters();
130+
131+
if (clearFilterLink.length) {
132+
clearFilterLink
133+
.toggleClass("disabled", !filtersActive)
134+
.toggleClass("btn-outline-secondary", !filtersActive)
135+
.toggleClass("btn-secondary", filtersActive)
136+
.attr("aria-disabled", !filtersActive)
137+
.css("pointer-events", filtersActive ? "auto" : "none");
138+
}
139+
};
140+
141+
$(form).on("input change", updateClearFiltersState);
142+
143+
$(document).on('click', '#clear, #clear_js', function (event) {
144+
event.preventDefault();
145+
if ($(this).attr("aria-disabled") === "true") {
146+
return;
147+
}
148+
const form = $(this).closest(".filter-set").find("form");
149+
if (form.length) {
150+
clear_form(form);
151+
// Refresh some UI components to work have cleared form from all respective libraries
152+
form.find("select.selectpicker").selectpicker("refresh");
153+
form.find(".multi-tag-input").val('').trigger('change');
154+
form.find("select.select2-hidden-accessible").val(null).trigger('change');
155+
form.find(".select2-selection__choice").remove();
156+
form.find(".select2-search__field").val('');
157+
// Update the state for the clear filters button
158+
updateClearFiltersState();
159+
160+
}
161+
});
162+
});
163+
</script>

tests/group_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def test_group_edit_name_and_global_role(self):
4343
driver.find_element(By.ID, "id_name").clear()
4444
driver.find_element(By.ID, "id_name").send_keys("Group Name")
4545
# click on 'apply filter' button
46-
driver.find_element(By.CSS_SELECTOR, "button.btn.btn-sm.btn-secondary").click()
46+
driver.find_element(By.ID, "apply").click()
4747
# only the needed group is now available, proceed with opening the context menu and clicking 'Edit' button
4848
driver.find_element(By.ID, "dropdownMenuGroup").click()
4949
driver.find_element(By.ID, "editGroup").click()
@@ -139,7 +139,7 @@ def test_group_delete(self):
139139
driver.find_element(By.ID, "id_name").clear()
140140
driver.find_element(By.ID, "id_name").send_keys("Another Name")
141141
# click on 'apply filter' button
142-
driver.find_element(By.CSS_SELECTOR, "button.btn.btn-sm.btn-secondary").click()
142+
driver.find_element(By.ID, "apply").click()
143143
# only the needed group is now available, proceed with clicking 'Delete' button
144144
driver.find_element(By.ID, "dropdownMenuGroup").click()
145145
driver.find_element(By.ID, "deleteGroup").click()

tests/product_group_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def navigate_to_group_view(self):
148148
driver.find_element(By.ID, "id_name").clear()
149149
driver.find_element(By.ID, "id_name").send_keys("Group Name")
150150
# click on 'apply filter' button
151-
driver.find_element(By.CSS_SELECTOR, "button.btn.btn-sm.btn-secondary").click()
151+
driver.find_element(By.ID, "apply").click()
152152
# only the needed group is now available, proceed with opening the context menu and clicking 'Edit' button
153153
driver.find_element(By.ID, "dropdownMenuGroup").click()
154154
driver.find_element(By.ID, "viewGroup").click()

tests/product_type_group_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def navigate_to_group_view(self):
147147
driver.find_element(By.ID, "id_name").clear()
148148
driver.find_element(By.ID, "id_name").send_keys("Group Name")
149149
# click on 'apply filter' button
150-
driver.find_element(By.CSS_SELECTOR, "button.btn.btn-sm.btn-secondary").click()
150+
driver.find_element(By.ID, "apply").click()
151151
# only the needed group is now available, proceed with opening the context menu and clicking 'Edit' button
152152
driver.find_element(By.ID, "dropdownMenuGroup").click()
153153
driver.find_element(By.ID, "viewGroup").click()

tests/user_test.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def test_user_edit_permissions(self):
114114
driver.find_element(By.ID, "id_username").clear()
115115
driver.find_element(By.ID, "id_username").send_keys("propersahm")
116116
# click on 'apply filter' button
117-
driver.find_element(By.CSS_SELECTOR, "button.btn.btn-sm.btn-secondary").click()
117+
driver.find_element(By.ID, "apply").click()
118118
# only the needed user is now available, proceed with opening the context menu and clicking 'Edit' button
119119
driver.find_element(By.ID, "dropdownMenuUser").click()
120120
driver.find_element(By.ID, "editUser").click()
@@ -141,7 +141,7 @@ def test_user_delete(self):
141141
driver.find_element(By.ID, "id_username").clear()
142142
driver.find_element(By.ID, "id_username").send_keys("propersahm")
143143
# click on 'apply filter' button
144-
driver.find_element(By.CSS_SELECTOR, "button.btn.btn-sm.btn-secondary").click()
144+
driver.find_element(By.ID, "apply").click()
145145
# only the needed user is now available, proceed with clicking 'View' button
146146
driver.find_element(By.ID, "dropdownMenuUser").click()
147147
driver.find_element(By.ID, "viewUser").click()
@@ -169,7 +169,7 @@ def test_user_with_writer_role_delete(self):
169169
driver.find_element(By.ID, "id_username").clear()
170170
driver.find_element(By.ID, "id_username").send_keys("userWriter")
171171
# click on 'apply filter' button
172-
driver.find_element(By.CSS_SELECTOR, "button.btn.btn-sm.btn-secondary").click()
172+
driver.find_element(By.ID, "apply").click()
173173
# only the needed user is now available, proceed with clicking 'View' button
174174
driver.find_element(By.ID, "dropdownMenuUser").click()
175175
driver.find_element(By.ID, "viewUser").click()
@@ -235,7 +235,7 @@ def test_user_edit_configuration(self):
235235
driver.find_element(By.ID, "id_username").clear()
236236
driver.find_element(By.ID, "id_username").send_keys("propersahm")
237237
# click on 'apply filter' button
238-
driver.find_element(By.CSS_SELECTOR, "button.btn.btn-sm.btn-secondary").click()
238+
driver.find_element(By.ID, "apply").click()
239239
# only the needed user is now available, proceed with opening the context menu and clicking 'Edit' button
240240
driver.find_element(By.ID, "dropdownMenuUser").click()
241241
driver.find_element(By.ID, "viewUser").click()
@@ -256,7 +256,7 @@ def test_user_edit_configuration(self):
256256
driver.find_element(By.ID, "id_username").clear()
257257
driver.find_element(By.ID, "id_username").send_keys("propersahm")
258258
# click on 'apply filter' button
259-
driver.find_element(By.CSS_SELECTOR, "button.btn.btn-sm.btn-secondary").click()
259+
driver.find_element(By.ID, "apply").click()
260260
# only the needed user is now available, proceed with opening the context menu and clicking 'Edit' button
261261
driver.find_element(By.ID, "dropdownMenuUser").click()
262262
driver.find_element(By.ID, "viewUser").click()

0 commit comments

Comments
 (0)