ui: js: Make select all checkbox option available more broadly

- Make the code work outside backups module.

- Move code to main.js so that any app can use this functionality.

- Make the code work for multiple such form fields in the same page.

- Use only pure JS, don't use jQuery.

- Add event handlers only after DOM content is loaded to avoid race conditions.

Tests performed:

- Checking the select-all button checks all options.

- De-checking the select-all button de-checks all options.

- De-checking one option when everything is checked, de-checks the select-all
button.

- Checking the last option when everything else is checked, checks the
select-all button.

- When loading a schedule page with all options checked, select-all button is
checked.

- When loading a schedule page with some option unchecked, select-all button is
unchecked.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Sunil Mohan Adapa 2021-01-05 22:03:34 -08:00 committed by James Valleroy
parent 77face68b0
commit 9b8388734d
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
6 changed files with 67 additions and 77 deletions

View File

@ -54,7 +54,7 @@ class CreateArchiveForm(forms.Form):
regex=r'^[^{}/]*$', required=False, strip=True)
selected_apps = forms.MultipleChoiceField(
label=_('Included apps'), help_text=_('Apps to include in the backup'),
widget=forms.CheckboxSelectMultiple)
widget=forms.CheckboxSelectMultiple(attrs={'class': 'has-select-all'}))
def __init__(self, *args, **kwargs):
"""Initialize the form with selectable apps."""
@ -71,7 +71,7 @@ class CreateArchiveForm(forms.Form):
class RestoreForm(forms.Form):
selected_apps = forms.MultipleChoiceField(
label=_('Select the apps you want to restore'),
widget=forms.CheckboxSelectMultiple)
widget=forms.CheckboxSelectMultiple(attrs={'class': 'has-select-all'}))
def __init__(self, *args, **kwargs):
"""Initialize the form with selectable apps."""

View File

@ -1,66 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
/*
This file is part of FreedomBox.
@licstart The following is the entire license notice for the
JavaScript code in this page.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
@licend The above is the entire license notice
for the JavaScript code in this page.
*/
function addSelectAll() {
let ul = document.getElementById('id_backups-selected_apps');
let li = document.createElement('li');
let label = document.createElement('label');
label.for = "select_all";
let checkbox = document.createElement('input');
checkbox.type = "checkbox";
checkbox.checked = "checked";
checkbox.id = "select-all";
label.appendChild(checkbox);
li.appendChild(label);
ul.insertBefore(li, ul.childNodes[0]);
};
addSelectAll();
/*
* When there is a change on the "select all" checkbox,set the
* checked property of all the checkboxes to the value of the
* "select all" checkbox
*/
$("#select-all").change(function() {
$("[type=checkbox]").prop('checked', $(this).prop("checked"));
});
$('[type=checkbox]').change(function() {
// If the rest of the checkbox items are checked check the "select all" checkbox as well
if ($('[type=checkbox]:checked').length == ($('[type=checkbox]').length - 1)) {
$("#select-all").prop('checked', true);
}
// Uncheck "select all" if one of the listed checkbox item is unchecked
if (false == $(this).prop("checked")) {
$("#select-all").prop('checked', false);
}
});

View File

@ -21,7 +21,3 @@
</form>
{% endblock %}
{% block page_js %}
<script type="text/javascript" src="{% static 'backups/select_all.js' %}"></script>
{% endblock %}

View File

@ -29,7 +29,3 @@
</p>
{% endblock %}
{% block page_js %}
<script type="text/javascript" src="{% static 'backups/select_all.js' %}"></script>
{% endblock %}

View File

@ -454,7 +454,8 @@ def backup_create(browser, app_name, archive_name=None):
buttons = browser.find_link_by_href('/plinth/sys/backups/create/')
submit(browser, buttons.first)
browser.find_by_id('select-all').uncheck()
eventually(browser.find_by_css, args=['.select-all'])
browser.find_by_css('.select-all').first.uncheck()
if archive_name:
browser.find_by_id('id_backups-name').fill(archive_name)

View File

@ -121,3 +121,66 @@ window.addEventListener('pageshow', function(event) {
element.remove();
}
});
/*
* Select all option for multiple checkboxes.
*/
document.addEventListener('DOMContentLoaded', function(event) {
let parents = document.querySelectorAll('ul.has-select-all');
for (const parent of parents) {
let li = document.createElement('li');
let label = document.createElement('label');
label.for = "select_all";
let checkbox = document.createElement('input');
checkbox.type = "checkbox";
checkbox.setAttribute('class', 'select-all');
label.appendChild(checkbox);
li.appendChild(label);
parent.insertBefore(li, parent.childNodes[0]);
setSelectAllValue(parent);
checkbox.addEventListener('change', onSelectAllChanged);
options = parent.querySelectorAll('input.has-select-all');
for (const option of options) {
option.addEventListener('change', onSelectAllOptionsChanged);
}
}
});
// When there is a change on the "select all" checkbox, set the checked property
// of all the checkboxes to the value of the "select all" checkbox
function onSelectAllChanged(event) {
const selectAllCheckbox = event.currentTarget;
const parent = selectAllCheckbox.parentElement.parentElement.parentElement;
const options = parent.querySelectorAll('input.has-select-all');
for (const option of options) {
option.checked = selectAllCheckbox.checked;
}
}
// When there is a change on a checkbox controlled by a select all checkbox,
// update the value of checkbox.
function onSelectAllOptionsChanged(event) {
const parent = event.currentTarget.parentElement.parentElement.parentElement;
setSelectAllValue(parent);
}
// Set/reset the checked property of "select all" checkbox based on whether all
// checkboxes it controls are checked.
function setSelectAllValue(parent) {
const options = parent.querySelectorAll('input.has-select-all');
let enableSelectAll = true;
for (const option of options) {
if (!option.checked) {
enableSelectAll = false;
break;
}
}
parent.querySelector('.select-all').checked = enableSelectAll;
}