diff --git a/app/webpacker/controllers/unsaved_changes_controller.js b/app/webpacker/controllers/unsaved_changes_controller.js index c458921065..7e2bc5055f 100644 --- a/app/webpacker/controllers/unsaved_changes_controller.js +++ b/app/webpacker/controllers/unsaved_changes_controller.js @@ -4,21 +4,34 @@ import { Controller } from "stimulus"; // // Usage : // - with beforeunload event : -//
// // - with turbolinks : -// // // You can also combine the two actions +// You also need to add 'data-action="change->unsaved-changes#formIsChanged"' on all the form element +// that can be interacted with +// +// Optional, you can add 'data-unsaved-changes-changed="true"' if you want to disable all +// submit buttons when the form hasn't been interacted with // export default class extends Controller { connect() { // disable submit button when first loading the page - if (!this.isFormChanged()) { + if (!this.isFormChanged() && this.isSubmitButtonDisabled()) { this.disableButtons(); } } @@ -27,7 +40,10 @@ export default class extends Controller { // We only do something if the form hasn't already been changed if (!this.isFormChanged()) { this.setChanged("true"); - this.enableButtons(); + + if (this.isSubmitButtonDisabled()) { + this.enableButtons(); + } } } @@ -63,6 +79,14 @@ export default class extends Controller { return this.data.get("changed") == "true"; } + isSubmitButtonDisabled() { + if (this.data.has("disable-submit-button")) { + return this.data.get("disable-submit-button") == "true"; + } + + return false; + } + enableButtons() { this.submitButtons().forEach((button) => { button.disabled = false; diff --git a/spec/javascripts/stimulus/unsaved_changes_controller_test.js b/spec/javascripts/stimulus/unsaved_changes_controller_test.js index b5748066c9..f6d8544ff7 100644 --- a/spec/javascripts/stimulus/unsaved_changes_controller_test.js +++ b/spec/javascripts/stimulus/unsaved_changes_controller_test.js @@ -13,7 +13,11 @@ describe("UnsavedChangesController", () => { beforeEach(() => { document.body.innerHTML = ` - @@ -21,16 +25,70 @@ describe("UnsavedChangesController", () => { }) describe("#connect", () => { - it("disables any submit button", () => { - const submit = document.getElementById("test-submit") + describe("when disable-submit-button is true", () => { + beforeEach(() => { + document.body.innerHTML = ` + + ` + }) - expect(submit.disabled).toBe(true) + it("disables any submit button", () => { + const submit = document.getElementById("test-submit") + + expect(submit.disabled).toBe(true) + }) + }) + + describe("when disable-submit-button is false", () => { + beforeEach(() => { + document.body.innerHTML = ` + + ` + }) + + it("doesn't disable any submit button", () => { + const submit = document.getElementById("test-submit") + + expect(submit.disabled).toBe(false) + }) + }) + + describe("when disable-submit-button is not set", () => { + it("doesn't disable any submit button", () => { + const submit = document.getElementById("test-submit") + + expect(submit.disabled).toBe(false) + }) }) }) describe("#formIsChanged", () => { + let checkbox + let submit + + beforeEach(() => { + checkbox = document.getElementById("test-checkbox") + submit = document.getElementById("test-submit") + }) + it("changed is set to true", () => { - const checkbox = document.getElementById("test-checkbox") const form = document.getElementById("test-form") checkbox.click() @@ -38,17 +96,28 @@ describe("UnsavedChangesController", () => { expect(form.dataset.unsavedChangesChanged).toBe("true") }) - it("enables any submit button", () => { - const checkbox = document.getElementById("test-checkbox") - const submit = document.getElementById("test-submit") + describe("when disable-submit-button is true", () => { + it("enables any submit button", () => { + checkbox.click() - checkbox.click() + expect(submit.disabled).toBe(false) + }) + }) - expect(submit.disabled).toBe(false) + describe("when disable-submit-button is false", () => { + it("does nothing", () => { + expect(submit.disabled).toBe(false) + + checkbox.click() + + expect(submit.disabled).toBe(false) + }) }) }) describe('#leavingPage', () => { + let checkbox + beforeEach(() => { // Add a mock I18n object to const mockedT = jest.fn() @@ -57,6 +126,8 @@ describe("UnsavedChangesController", () => { global.I18n = { t: mockedT } + + checkbox = document.getElementById("test-checkbox") }) afterEach(() => { @@ -65,8 +136,6 @@ describe("UnsavedChangesController", () => { describe('when triggering a beforeunload event', () => { it("triggers leave page pop up when leaving page and form has been interacted with", () => { - const checkbox = document.getElementById("test-checkbox") - // interact with the form checkbox.click() @@ -81,9 +150,18 @@ describe("UnsavedChangesController", () => { }) describe('when triggering a turbolinks:before-visit event', () => { + let confirmSpy + + beforeEach(() => { + confirmSpy = jest.spyOn(window, 'confirm') + }) + + afterEach(() => { + // cleanup + confirmSpy.mockRestore() + }) + it("triggers a confirm popup up when leaving page and form has been interacted with", () => { - const checkbox = document.getElementById("test-checkbox") - const confirmSpy = jest.spyOn(window, 'confirm') confirmSpy.mockImplementation((msg) => {}) // interact with the form @@ -94,12 +172,9 @@ describe("UnsavedChangesController", () => { window.dispatchEvent(turbolinkEv) expect(confirmSpy).toHaveBeenCalled() - }) it("stays on the page if user clicks cancel on the confirm popup", () => { - const checkbox = document.getElementById("test-checkbox") - const confirmSpy = jest.spyOn(window, 'confirm') // return false to simulate a user clicking on cancel confirmSpy.mockImplementation((msg) => (false)) @@ -114,9 +189,6 @@ describe("UnsavedChangesController", () => { expect(confirmSpy).toHaveBeenCalled() expect(preventDefaultSpy).toHaveBeenCalled() - - // cleanup - confirmSpy.mockRestore() }) }) })