From bfd6319cf2d3e2cb2630826ce793df836a21e04e Mon Sep 17 00:00:00 2001 From: David Cook Date: Thu, 29 Feb 2024 12:20:50 +1100 Subject: [PATCH] Mark tom-select as changed Thankfully I was able to use basic DOM features, so there's no coupling of the logic with tom-select. It wasn't going to be simple to get tom-select to listen for the 'changed' class on the original select, so I found a simple solution with a CSS sibling selector instead. --- .../controllers/bulk_form_controller.js | 20 ++++++++++++++++- .../css/admin_v3/components/tom_select.scss | 9 ++++++++ .../stimulus/bulk_form_controller_test.js | 22 ++++++++++++++++++- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/app/webpacker/controllers/bulk_form_controller.js b/app/webpacker/controllers/bulk_form_controller.js index 98a6063a0e..1f2aa80f0e 100644 --- a/app/webpacker/controllers/bulk_form_controller.js +++ b/app/webpacker/controllers/bulk_form_controller.js @@ -1,6 +1,19 @@ import { Controller } from "stimulus"; -// Manages "changed" state for a form with multiple records +// Manage "changed" state for a form with multiple records +// +// When any elements are changed: +// - the element is marked ".changed" +// - "actions" element appears +// - "changedSummary" element is updated using I18n +// - "disableSelector" elements are disabled +// - The browser will warn if trying to leave the page +// +// Supported element types: +// - input[type=text] and similar +// - input[type=checkbox] +// - select (single) - including tom-select +// export default class BulkFormController extends Controller { static targets = ["actions", "changedSummary"]; static values = { @@ -113,6 +126,11 @@ export default class BulkFormController extends Controller { #isChanged(element) { if (element.type == "checkbox") { return element.defaultChecked !== undefined && element.checked != element.defaultChecked; + + } else if (element.type == "select-one") { + const defaultSelected = Array.from(element.options).find((opt)=>opt.hasAttribute('selected')); + return element.selectedOptions[0] != defaultSelected; + } else { return element.defaultValue !== undefined && element.value != element.defaultValue; } diff --git a/app/webpacker/css/admin_v3/components/tom_select.scss b/app/webpacker/css/admin_v3/components/tom_select.scss index f63b63898d..c616e8d5f4 100644 --- a/app/webpacker/css/admin_v3/components/tom_select.scss +++ b/app/webpacker/css/admin_v3/components/tom_select.scss @@ -188,3 +188,12 @@ } } } + +// Display as "changed" if sibling select is marked as changed. +select.changed + .ts-wrapper { + &.single, &.multi { + .ts-control { + border-color: $color-txt-changed-brd; + } + } +} diff --git a/spec/javascripts/stimulus/bulk_form_controller_test.js b/spec/javascripts/stimulus/bulk_form_controller_test.js index 7571191708..82d83c7271 100644 --- a/spec/javascripts/stimulus/bulk_form_controller_test.js +++ b/spec/javascripts/stimulus/bulk_form_controller_test.js @@ -36,6 +36,10 @@ describe("BulkFormController", () => {
+
@@ -47,7 +51,7 @@ describe("BulkFormController", () => { }); describe("marking changed fields", () => { - it("onInput", () => { + it("input: onInput", () => { input1a.value = 'updated1a'; input1a.dispatchEvent(new Event("input")); // Expect only first field to show changed @@ -59,7 +63,23 @@ describe("BulkFormController", () => { input1a.value = 'initial1a'; input1a.dispatchEvent(new Event("input")); expect(input1a.classList).not.toContain('changed'); + }); + it("select: onInput", () => { + // Select a different option (it's the only way in Jest..) + select1.options[0].selected = true; + select1.options[1].selected = false; + select1.dispatchEvent(new Event("input")); + // Expect select to show changed + expect(input1a.classList).not.toContain('changed'); + expect(input1b.classList).not.toContain('changed'); + expect(select1.classList).toContain('changed'); + + // Change back to original value + select1.options[0].selected = false; + select1.options[1].selected = true; + select1.dispatchEvent(new Event("input")); + expect(select1.classList).not.toContain('changed'); }); it("multiple fields", () => {