From 585a08fea065e8c33a31ea1f14637aa4aa6cd45d Mon Sep 17 00:00:00 2001 From: LB Johnston Date: Tue, 27 Jun 2023 21:02:00 +1000 Subject: [PATCH] Adopt w-bulk Stimulus controller for form submissions listing --- CHANGELOG.txt | 1 + client/src/controllers/BulkController.test.js | 39 ++++++++++++- client/src/controllers/BulkController.ts | 48 ++++++++++++++-- docs/releases/5.2.md | 1 + .../wagtailforms/list_submissions.html | 8 +-- .../wagtailforms/submissions_index.html | 56 ------------------- 6 files changed, 86 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 7984cc2b2325..4fd1a8d35e42 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -31,6 +31,7 @@ Changelog * Maintenance: Allow `ViewSet` subclasses to customise `url_prefix` and `url_namespace` logic (Matt Westcott) * Maintenance: Simplify `SnippetViewSet` registration code (Sage Abdullah) * Maintenance: Rename groups `IndexView.results_template_name` to `results.html` (Sage Abdullah) + * Maintenance: Migrate form submission listing checkbox toggling to the shared `w-bulk` Stimulus implementation (LB (Ben) Johnston) 5.1.1 (14.08.2023) diff --git a/client/src/controllers/BulkController.test.js b/client/src/controllers/BulkController.test.js index df956401c234..435e79337476 100644 --- a/client/src/controllers/BulkController.test.js +++ b/client/src/controllers/BulkController.test.js @@ -4,7 +4,7 @@ import { BulkController } from './BulkController'; describe('BulkController', () => { beforeEach(() => { document.body.innerHTML = ` -
+
@@ -115,4 +115,41 @@ describe('BulkController', () => { expect(itemCheckbox.checked).toBe(true); }); }); + + it('should allow for action targets to have classes toggled when any checkboxes are clicked', async () => { + const container = document.getElementById('bulk-container'); + + // create innerActions container that will be conditionally hidden with test classes + container.setAttribute( + 'data-w-bulk-action-inactive-class', + 'hidden w-invisible', + ); + const innerActions = document.createElement('div'); + innerActions.id = 'inner-actions'; + innerActions.className = 'keep-me hidden w-invisible'; + innerActions.setAttribute('data-w-bulk-target', 'action'); + container.prepend(innerActions); + + const innerActionsElement = document.getElementById('inner-actions'); + + expect( + document + .getElementById('checkboxes') + .querySelectorAll(':checked:not(:disabled)').length, + ).toEqual(0); + + expect(innerActionsElement.className).toEqual('keep-me hidden w-invisible'); + + const firstCheckbox = document + .getElementById('checkboxes') + .querySelector("[type='checkbox']:not([disabled])"); + + firstCheckbox.click(); + + expect(innerActionsElement.className).toEqual('keep-me'); + + firstCheckbox.click(); + + expect(innerActionsElement.className).toEqual('keep-me hidden w-invisible'); + }); }); diff --git a/client/src/controllers/BulkController.ts b/client/src/controllers/BulkController.ts index 2f29eab3c624..398f79a6ca8c 100644 --- a/client/src/controllers/BulkController.ts +++ b/client/src/controllers/BulkController.ts @@ -1,21 +1,40 @@ import { Controller } from '@hotwired/stimulus'; + /** * Adds the ability to collectively toggle a set of (non-disabled) checkboxes. * - * @example + * @example - Basic usage *
* *
- * - * - * + * + * + * *
* * *
+ * + * @example - Showing and hiding an actions container + *
+ *
+ * + *
+ * + *
+ * + * + * + *
+ *
+ */ export class BulkController extends Controller { - static targets = ['all', 'item']; + static classes = ['actionInactive']; + static targets = ['action', 'all', 'item']; + + /** Target(s) that will have the `actionInactive` classes removed if any actions are checked */ + declare readonly actionTargets: HTMLElement[]; /** All select-all checkbox targets */ declare readonly allTargets: HTMLInputElement[]; @@ -23,6 +42,9 @@ export class BulkController extends Controller { /** All item checkbox targets */ declare readonly itemTargets: HTMLInputElement[]; + /** Classes to remove on the actions target if any actions are checked */ + declare readonly actionInactiveClasses: string[]; + get activeItems() { return this.itemTargets.filter(({ disabled }) => !disabled); } @@ -36,13 +58,27 @@ export class BulkController extends Controller { /** * When something is toggled, ensure the select all targets are kept in sync. + * Update the classes on the action targets to reflect the current state. */ toggle() { - const isAllChecked = !this.activeItems.some((item) => !item.checked); + const activeItems = this.activeItems; + const totalCheckedItems = activeItems.filter((item) => item.checked).length; + const isAnyChecked = totalCheckedItems > 0; + const isAllChecked = totalCheckedItems === activeItems.length; + this.allTargets.forEach((target) => { // eslint-disable-next-line no-param-reassign target.checked = isAllChecked; }); + + const actionInactiveClasses = this.actionInactiveClasses; + if (!actionInactiveClasses.length) return; + + this.actionTargets.forEach((element) => { + actionInactiveClasses.forEach((actionInactiveClass) => { + element.classList.toggle(actionInactiveClass, !isAnyChecked); + }); + }); } /** diff --git a/docs/releases/5.2.md b/docs/releases/5.2.md index fd7518c476bb..5827f239da6d 100644 --- a/docs/releases/5.2.md +++ b/docs/releases/5.2.md @@ -50,6 +50,7 @@ depth: 1 * Allow `ViewSet` subclasses to customise `url_prefix` and `url_namespace` logic (Matt Westcott) * Simplify `SnippetViewSet` registration code (Sage Abdullah) * Rename groups `IndexView.results_template_name` to `results.html` (Sage Abdullah) + * Migrate form submission listing checkbox toggling to the shared `w-bulk` Stimulus implementation (LB (Ben) Johnston) ## Upgrade considerations - changes affecting all projects diff --git a/wagtail/contrib/forms/templates/wagtailforms/list_submissions.html b/wagtail/contrib/forms/templates/wagtailforms/list_submissions.html index 8ddcfca0e3e9..28d3e30a6198 100644 --- a/wagtail/contrib/forms/templates/wagtailforms/list_submissions.html +++ b/wagtail/contrib/forms/templates/wagtailforms/list_submissions.html @@ -1,17 +1,17 @@ {% load i18n %}
- +
- + {% for heading in data_headings %} {% for cell in row.fields %}
- +
{% if heading.order %}{{ heading.label }}{% else %}{{ heading.label }}{% endif %} @@ -23,7 +23,7 @@ {% for row in data_rows %}
- + diff --git a/wagtail/contrib/forms/templates/wagtailforms/submissions_index.html b/wagtail/contrib/forms/templates/wagtailforms/submissions_index.html index 663e56f67c6f..335e039d0cc9 100644 --- a/wagtail/contrib/forms/templates/wagtailforms/submissions_index.html +++ b/wagtail/contrib/forms/templates/wagtailforms/submissions_index.html @@ -21,62 +21,6 @@ timepicker: false, format: 'Y-m-d', }); - - var selectAllCheckbox = document.getElementById('select-all'); - var deleteButton = document.getElementById('delete-submissions'); - - function updateActions() { - var submissionCheckboxes = $('input[type=checkbox].select-submission'); - var someSubmissionsSelected = submissionCheckboxes.is(':checked'); - var everySubmissionSelected = !submissionCheckboxes.is(':not(:checked)'); - - // Select all box state - if (everySubmissionSelected) { - // Every submission has been selected - selectAllCheckbox.checked = true; - selectAllCheckbox.indeterminate = false; - } else if (someSubmissionsSelected) { - // At least one, but not all submissions have been selected - selectAllCheckbox.checked = false; - selectAllCheckbox.indeterminate = true; - } else { - // No submissions have been selected - selectAllCheckbox.checked = false; - selectAllCheckbox.indeterminate = false; - } - - // Delete button state - if (someSubmissionsSelected) { - deleteButton.classList.remove('disabled') - deleteButton.style.visibility = "visible"; - } else { - deleteButton.classList.add('disabled') - deleteButton.style.visibility = "hidden"; - } - } - - - // Event handlers - - $(selectAllCheckbox).on('change', function() { - let checked = this.checked; - - // Update checkbox states - $('input[type=checkbox].select-submission').each(function() { - this.checked = checked; - }); - - updateActions(); - }); - - $('input[type=checkbox].select-submission').on('change', function() { - updateActions(); - }); - - // initial call to updateActions to bring delete button state in sync with checkboxes - // in the case that some checkboxes are pre-checked (which will be the case in some - // browsers when using the back button) - updateActions(); }); {% endblock %}