diff --git a/app/assets/javascripts/notifyModal.js b/app/assets/javascripts/notifyModal.js index bdf7fa48a..ccda2a1a2 100644 --- a/app/assets/javascripts/notifyModal.js +++ b/app/assets/javascripts/notifyModal.js @@ -61,20 +61,20 @@ function closeModal() { } -// Attach open triggers -document.querySelectorAll('[data-open-modal]').forEach(btn => { - btn.addEventListener('click', () => { - const modalId = btn.getAttribute('data-open-modal'); - openModal(modalId); +function attachModalTriggers() { + document.querySelectorAll('[data-open-modal]').forEach(btn => { + btn.addEventListener('click', () => { + const modalId = btn.getAttribute('data-open-modal'); + openModal(modalId); + }); }); -}); -// Attach close triggers -document.querySelectorAll('[data-close-modal]').forEach(btn => { - btn.addEventListener('click', () => { - closeModal(); + document.querySelectorAll('[data-close-modal]').forEach(btn => { + btn.addEventListener('click', () => { + closeModal(); + }); }); -}); +} // Escape key closes modal document.addEventListener('keydown', (e) => { @@ -89,3 +89,12 @@ document.addEventListener('click', (e) => { closeModal(); } }); + +document.addEventListener('DOMContentLoaded', () => { + attachModalTriggers(); +}); + +// ✅ Check if we're in a Node.js environment (for Jest) before using `module.exports` +if (typeof module !== "undefined" && typeof module.exports !== "undefined") { + module.exports = { closeModal, openModal, attachModalTriggers }; +} diff --git a/app/assets/javascripts/preventDuplicateFormSubmissions.js b/app/assets/javascripts/preventDuplicateFormSubmissions.js index 4c4b97252..c62aaa9ec 100644 --- a/app/assets/javascripts/preventDuplicateFormSubmissions.js +++ b/app/assets/javascripts/preventDuplicateFormSubmissions.js @@ -21,6 +21,12 @@ $submitButton.append(''); } + // Disable Cancel button too + const $cancelButton = $('button[name]').filter(function () { + return $(this).attr('name')?.toLowerCase() === 'cancel'; + }); + $cancelButton.prop('disabled', true); + setTimeout(() => { renableSubmitButton($submitButton); }, 10000); // fallback safety diff --git a/app/assets/sass/uswds/_main.scss b/app/assets/sass/uswds/_main.scss index 61e319f27..585d0ee63 100644 --- a/app/assets/sass/uswds/_main.scss +++ b/app/assets/sass/uswds/_main.scss @@ -352,6 +352,19 @@ h2.recipient-list { } // Button ellipses loading +.dot-anim { + display: inline-block; + margin-left: 0; /* remove left margin if it exists */ + padding-left: 0; + font-size: 1em; + animation: dots 1s steps(3, end) infinite; +} + +/* Optional: reduce spacing by removing whitespace node */ +button span.dot-anim { + margin-left: 0; /* forces no space even if white-space exists */ +} + .dot-anim::after { content: '.'; animation: dotPulse 1.5s steps(3, end) infinite; diff --git a/tests/javascripts/notifyModal.test.js b/tests/javascripts/notifyModal.test.js index df1aba404..be703fd97 100644 --- a/tests/javascripts/notifyModal.test.js +++ b/tests/javascripts/notifyModal.test.js @@ -2,7 +2,7 @@ * @jest-environment jsdom */ -const { openModal, closeModal } = require("../../app/assets/javascripts/modal"); // adjust path if needed +const { openModal, closeModal, attachModalTriggers } = require("../../app/assets/javascripts/notifyModal.js"); // adjust path if needed describe("Modal functionality", () => { let modalWrapper, modalElement, openBtn, closeBtn, anotherFocusable; @@ -118,3 +118,44 @@ describe("Modal functionality", () => { expect(modalWrapper.classList.contains("is-hidden")).toBe(true); }); }); + +describe("Modal trigger buttons", () => { + beforeEach(() => { + document.body.innerHTML = ` + + + `; + }); + + afterEach(() => { + document.body.innerHTML = ""; + }); + + test("Clicking [data-open-modal] opens the modal", () => { + attachModalTriggers(); + const openButton = document.querySelector('[data-open-modal]'); + openButton.click(); + + const modalWrapper = document.getElementById("myModal"); + expect(modalWrapper.classList.contains("is-hidden")).toBe(false); + }); + + test("Clicking [data-close-modal] closes the modal", () => { + const modalWrapper = document.getElementById("myModal"); + modalWrapper.classList.remove("is-hidden"); + + attachModalTriggers(); + const closeButton = document.querySelector('[data-close-modal]'); + closeModal(); // ensure modal is open to begin with + openModal("myModal"); + + closeButton.click(); + expect(modalWrapper.classList.contains("is-hidden")).toBe(true); + }); + });