diff --git a/app/views/admin/products_v3/_table.html.haml b/app/views/admin/products_v3/_table.html.haml index 454bb2ed7f..9b7adebe2a 100644 --- a/app/views/admin/products_v3/_table.html.haml +++ b/app/views/admin/products_v3/_table.html.haml @@ -88,13 +88,13 @@ %td.field.on-hand__wrapper{'data-controller': "popout"} %button.on-hand__button{'data-popout-target': "button"} = variant.on_demand ? t(:on_demand) : variant.on_hand - %div.on-hand__popout{ style: 'display: none;', 'data-popout-target': "dialog"} + %div.on-hand__popout{ style: 'display: none;', 'data-controller': 'toggle-control', 'data-popout-target': "dialog" } .field - = variant_form.number_field :on_hand, 'aria-label': t('admin.products_page.columns.on_hand') + = variant_form.number_field :on_hand, 'aria-label': t('admin.products_page.columns.on_hand'), 'data-toggle-control-target': 'control', disabled: variant_form.object.on_demand = error_message_on variant, :on_hand .field.checkbox = variant_form.label :on_demand do - = variant_form.check_box :on_demand, 'data-action': 'change->popout#closeIfChecked' + = variant_form.check_box :on_demand, 'data-action': 'change->toggle-control#disableIfPresent change->popout#closeIfChecked' = t('admin.products_page.columns.on_demand') %td.align-left .content= variant.product.supplier&.name # same as product diff --git a/app/webpacker/controllers/toggle_control_controller.js b/app/webpacker/controllers/toggle_control_controller.js new file mode 100644 index 0000000000..c9a64d9b29 --- /dev/null +++ b/app/webpacker/controllers/toggle_control_controller.js @@ -0,0 +1,33 @@ +import { Controller } from "stimulus"; + +export default class extends Controller { + static targets = ["control"]; + + disableIfPresent(event) { + const input = event.currentTarget; + const disable = !!this.#inputValue(input); // Coerce value to boolean + + this.controlTargets.forEach((target) => { + target.disabled = disable; + }); + + // Focus when enabled + if (!disable) { + this.controlTargets[0].focus(); + } + } + + //todo: can a new method disableIfBlank replace ButtonDisabledController? + //todo: can a new method toggleDisplay replace ToggleController? + //todo: can toggleDisplay with optional chevron-target replace RemoteToggleController? + + // private + + // Return input's value, but only if it would be submitted by a form + // Radio buttons not supported (yet) + #inputValue(input) { + if (input.type != "checkbox" || input.checked) { + return input.value; + } + } +} diff --git a/spec/javascripts/stimulus/toggle_control_controller_test.js b/spec/javascripts/stimulus/toggle_control_controller_test.js new file mode 100644 index 0000000000..eeffc17ae6 --- /dev/null +++ b/spec/javascripts/stimulus/toggle_control_controller_test.js @@ -0,0 +1,70 @@ +/** + * @jest-environment jsdom + */ + +import { Application } from "stimulus"; +import toggle_controller from "../../../app/webpacker/controllers/toggle_control_controller"; + +describe("ToggleControlController", () => { + beforeAll(() => { + const application = Application.start(); + application.register("toggle-control", toggle_controller); + }); + + describe("#disableIfPresent", () => { + describe("with checkbox", () => { + beforeEach(() => { + document.body.innerHTML = `
+ + +
`; + + const checkbox = document.getElementById("checkbox"); + const control = document.getElementById("control"); + }); + + it("Disables when checkbox is checked", () => { + checkbox.click(); + expect(checkbox.checked).toBe(true); + + expect(control.disabled).toBe(true); + }); + + it("Enables when checkbox is un-checked", () => { + checkbox.click(); + checkbox.click(); + expect(checkbox.checked).toBe(false); + + expect(control.disabled).toBe(false); + }); + }); + describe("with input", () => { + beforeEach(() => { + document.body.innerHTML = `
+ + +
`; + + const input = document.getElementById("input"); + const control = document.getElementById("control"); + }); + + it("Disables when input is filled", () => { + input.value = "test" + input.dispatchEvent(new Event("input")); + + expect(control.disabled).toBe(true); + }); + + it("Enables when input is emptied", () => { + input.value = "test" + input.dispatchEvent(new Event("input")); + + input.value = "" + input.dispatchEvent(new Event("input")); + + expect(control.disabled).toBe(false); + }); + }); + }); +});