From 18e40bebd0939162bd188600b2b2090b6bb872bd Mon Sep 17 00:00:00 2001 From: David Cook Date: Thu, 7 Sep 2023 16:27:52 +1000 Subject: [PATCH] Mark modified fields --- app/views/admin/products_v3/_table.html.haml | 4 +- .../controllers/bulk_form_controller.js | 21 ++++++++ app/webpacker/css/admin/products_v3.scss | 4 ++ .../css/admin_v3/globals/variables.scss | 1 + app/webpacker/css/admin_v3/shared/forms.scss | 4 ++ .../stimulus/bulk_form_controller_test.js | 49 +++++++++++++++++++ 6 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 app/webpacker/controllers/bulk_form_controller.js create mode 100644 spec/javascripts/stimulus/bulk_form_controller_test.js diff --git a/app/views/admin/products_v3/_table.html.haml b/app/views/admin/products_v3/_table.html.haml index 7c3aa22d5a..a200a8cd41 100644 --- a/app/views/admin/products_v3/_table.html.haml +++ b/app/views/admin/products_v3/_table.html.haml @@ -1,6 +1,6 @@ = form_with url: bulk_update_admin_products_v3_index_path, method: :patch, id: "products-form", - - html: {'data-reflex-serialize-form': true, 'data-reflex': 'submit->products#bulk_update'} do |form| + html: {'data-reflex-serialize-form': true, 'data-reflex': 'submit->products#bulk_update', + 'data-controller': "bulk-form"} do |form| %fieldset.form-actions .container .status.ten.columns diff --git a/app/webpacker/controllers/bulk_form_controller.js b/app/webpacker/controllers/bulk_form_controller.js new file mode 100644 index 0000000000..163e46cde1 --- /dev/null +++ b/app/webpacker/controllers/bulk_form_controller.js @@ -0,0 +1,21 @@ +import { Controller } from "stimulus"; + +// Manages "modified" state for a form with multiple records +export default class BulkFormController extends Controller { + connect() { + this.form = this.element; + + // Start listening for any changes within the form + // this.element.addEventListener('change', this.toggleModified.bind(this)); // dunno why this doesn't work + for (const element of this.form.elements) { + element.addEventListener("keyup", this.toggleModified.bind(this)); // instant response + element.addEventListener("change", this.toggleModified.bind(this)); // just in case (eg right-click paste) + } + } + + toggleModified(e) { + const element = e.target; + const changed = element.value != element.defaultValue; + element.classList.toggle("modified", changed); + } +} diff --git a/app/webpacker/css/admin/products_v3.scss b/app/webpacker/css/admin/products_v3.scss index c673cbe139..47ef952974 100644 --- a/app/webpacker/css/admin/products_v3.scss +++ b/app/webpacker/css/admin/products_v3.scss @@ -100,6 +100,10 @@ &:focus { border-color: $color-txt-hover-brd; } + + &.modified { + border-color: $color-txt-modified-brd; + } } .field_with_errors { diff --git a/app/webpacker/css/admin_v3/globals/variables.scss b/app/webpacker/css/admin_v3/globals/variables.scss index 7706a4073d..b30f7ac140 100644 --- a/app/webpacker/css/admin_v3/globals/variables.scss +++ b/app/webpacker/css/admin_v3/globals/variables.scss @@ -74,6 +74,7 @@ $color-sel-hover-text: $white !default; $color-txt-brd: $color-border !default; $color-txt-text: $near-black !default; $color-txt-hover-brd: $teal !default; +$color-txt-modified-brd: $bright-orange !default; $vpadding-txt: 5px; $hpadding-txt: 8px; diff --git a/app/webpacker/css/admin_v3/shared/forms.scss b/app/webpacker/css/admin_v3/shared/forms.scss index b9abfd6068..570fa95c16 100644 --- a/app/webpacker/css/admin_v3/shared/forms.scss +++ b/app/webpacker/css/admin_v3/shared/forms.scss @@ -21,6 +21,10 @@ fieldset { &[disabled] { opacity: 0.7; } + + &.modified { + border-color: $color-txt-modified-brd; + } } textarea { diff --git a/spec/javascripts/stimulus/bulk_form_controller_test.js b/spec/javascripts/stimulus/bulk_form_controller_test.js new file mode 100644 index 0000000000..0a2ce229da --- /dev/null +++ b/spec/javascripts/stimulus/bulk_form_controller_test.js @@ -0,0 +1,49 @@ +/** + * @jest-environment jsdom + */ + +import { Application } from "stimulus"; +import bulk_form_controller from "../../../app/webpacker/controllers/bulk_form_controller"; + +describe("BulkFormController", () => { + beforeAll(() => { + const application = Application.start(); + application.register("bulk-form", bulk_form_controller); + }); + + beforeEach(() => { + document.body.innerHTML = ` +
+ + +
+ `; + }); + + describe("#toggleModified", () => { + it("marks a changed element as modified", () => { + // const form = document.getElementsByTagName("form")[0]; + const input1 = document.getElementById("input1"); + const input2 = document.getElementById("input2"); + + expect(input1.classList).not.toContain('modified'); + expect(input2.classList).not.toContain('modified'); + + // Value has been changed (we're not simulating a user in a browser here; we're testing DOM events directly) + input1.value = 'updated1'; + input1.dispatchEvent(new Event("change")); + // form.dispatchEvent(new Event("change")); + + expect(input1.classList).toContain('modified'); + expect(input2.classList).not.toContain('modified'); + + // Change back to original value + input1.value = 'initial1'; + input1.dispatchEvent(new Event("change")); + // form.dispatchEvent(new Event("change")); + + expect(input1.classList).not.toContain('modified'); + expect(input2.classList).not.toContain('modified'); + }); + }); +});