From cc06e429548c51edd94c80334f86977749b93a82 Mon Sep 17 00:00:00 2001 From: "G. Torres" Date: Tue, 29 Oct 2024 17:02:39 -0400 Subject: [PATCH 1/7] Adds event listeners to listen for unsaved edits and alerting the user if there are unsaved edits --- app/assets/javascripts/stories.js | 42 +++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/app/assets/javascripts/stories.js b/app/assets/javascripts/stories.js index 5e7da51b..4b726ae5 100644 --- a/app/assets/javascripts/stories.js +++ b/app/assets/javascripts/stories.js @@ -25,8 +25,50 @@ document.addEventListener("DOMContentLoaded", () => { debounceTimer = window.setTimeout(updateMarkdown, 300); }); }); + + const form = document.querySelector('.edit_story'); + const backButton = document.getElementById('back'); + const logo = document.getElementById('logo'); + let isDirty = false; + + // Mark the form as dirty when any input changes + form.addEventListener('input', function () { + isDirty = true; + }); + + // Attach a click event to the custom back button + [backButton, logo].forEach(element => { + element.addEventListener('click', function (event) { + if (isDirty) { + const confirmLeave = confirm("You have unsaved changes. Are you sure you want to go back?"); + if (!confirmLeave) { + // Prevent navigation if the user chooses not to leave + event.preventDefault(); + } else { + // Optionally, reset isDirty if leaving + isDirty = false; + } + } + }) + }); + + // Reset isDirty on form submission + form.addEventListener('submit', function () { + isDirty = false; + }); + + if (isDirty) { + window.addEventListener("beforeunload", warnUserifUnsavedEdits); + } else { + window.removeEventListener("beforeunload", warnUserifUnsavedEdits); + } }); +function warnUserifUnsavedEdits(event) { + event.preventDefault(); + event.returnValue = ''; +} + function updateStatusButton(color, status) { const button = document.querySelector(".story-title .dropdown-wrapper > button"); button.className = `button ${color}`; From 7051f1533eec74b8cc85deaa4c033626dda350f1 Mon Sep 17 00:00:00 2001 From: "G. Torres" Date: Wed, 30 Oct 2024 10:09:22 -0400 Subject: [PATCH 2/7] Adds spec to test for presence of alert when we try to navigate away from an unsaved edited story --- spec/features/stories_manage_spec.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spec/features/stories_manage_spec.rb b/spec/features/stories_manage_spec.rb index 5048201f..22cf0c98 100644 --- a/spec/features/stories_manage_spec.rb +++ b/spec/features/stories_manage_spec.rb @@ -68,6 +68,17 @@ expect(page).to have_content "Story updated!" end + it "alerts me when I try to navigate away from the page without saving my edits", js: true do + visit project_path(id: project.id) + click_button "More actions" + click_link "Edit" + fill_in "story[title]", with: "As a user, I want to edit stories" + click_link "Back" + alert_text = page.driver.browser.switch_to.alert.text + + expect(alert_text).to eq "You have unsaved changes. Are you sure you want to go back?" + end + it "allows me to delete a story" do visit project_path(id: project.id) From 7b5afd6bfe87c9ded5abd075bd0b6a938dfb5ab0 Mon Sep 17 00:00:00 2001 From: "G. Torres" Date: Thu, 7 Nov 2024 15:03:13 -0500 Subject: [PATCH 3/7] Refactored the code to only add the event listeners on the form if the form existed. Also created a function to add and remove the beforeunload event listener --- app/assets/javascripts/stories.js | 35 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/app/assets/javascripts/stories.js b/app/assets/javascripts/stories.js index 4b726ae5..f06b95e6 100644 --- a/app/assets/javascripts/stories.js +++ b/app/assets/javascripts/stories.js @@ -26,19 +26,28 @@ document.addEventListener("DOMContentLoaded", () => { }); }); - const form = document.querySelector('.edit_story'); - const backButton = document.getElementById('back'); - const logo = document.getElementById('logo'); + const form = document.querySelector(".edit_story"); + const backButton = document.getElementById("back"); + const logo = document.getElementById("logo"); let isDirty = false; - // Mark the form as dirty when any input changes - form.addEventListener('input', function () { - isDirty = true; - }); + if (form) { + // Mark the form as dirty when any input changes + form.addEventListener("input", function () { + isDirty = true; + addBeforeUnloadEventListener(isDirty); + }); + + // Reset isDirty on form submission + form.addEventListener("submit", function () { + isDirty = false; + addBeforeUnloadEventListener(isDirty); + }); + } // Attach a click event to the custom back button [backButton, logo].forEach(element => { - element.addEventListener('click', function (event) { + element.addEventListener("click", function (event) { if (isDirty) { const confirmLeave = confirm("You have unsaved changes. Are you sure you want to go back?"); if (!confirmLeave) { @@ -47,22 +56,20 @@ document.addEventListener("DOMContentLoaded", () => { } else { // Optionally, reset isDirty if leaving isDirty = false; + addBeforeUnloadEventListener(isDirty) } } }) }); +}); - // Reset isDirty on form submission - form.addEventListener('submit', function () { - isDirty = false; - }); - +function addBeforeUnloadEventListener(isDirty) { if (isDirty) { window.addEventListener("beforeunload", warnUserifUnsavedEdits); } else { window.removeEventListener("beforeunload", warnUserifUnsavedEdits); } -}); +} function warnUserifUnsavedEdits(event) { event.preventDefault(); From b101ad753259752c2c392c1265dca4d13bb2a76c Mon Sep 17 00:00:00 2001 From: "G. Torres" Date: Fri, 5 Sep 2025 19:47:12 -0400 Subject: [PATCH 4/7] DT-340: Edit tests so that it tests all scenarios --- spec/features/stories_manage_spec.rb | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/spec/features/stories_manage_spec.rb b/spec/features/stories_manage_spec.rb index 22cf0c98..f9f59501 100644 --- a/spec/features/stories_manage_spec.rb +++ b/spec/features/stories_manage_spec.rb @@ -72,11 +72,26 @@ visit project_path(id: project.id) click_button "More actions" click_link "Edit" - fill_in "story[title]", with: "As a user, I want to edit stories" + click_link "Back" - alert_text = page.driver.browser.switch_to.alert.text + assert_current_path project_path(id: project.id) + + click_button "More actions" + click_link "Edit" + + fill_in "story[title]", with: "As a user, I want to edit stories" + + dismiss_confirm("You have unsaved changes. Are you sure you want to go back?") do + find("#logo").click + end + + assert_current_path edit_project_story_path(project, story) + + accept_confirm("You have unsaved changes. Are you sure you want to go back?") do + click_link "Back" + end - expect(alert_text).to eq "You have unsaved changes. Are you sure you want to go back?" + assert_current_path project_path(id: project.id) end it "allows me to delete a story" do From 2089a1de28268288c7a7c72c2ea061763fe28274 Mon Sep 17 00:00:00 2001 From: "G. Torres" Date: Fri, 5 Sep 2025 20:01:39 -0400 Subject: [PATCH 5/7] Update app/assets/javascripts/stories.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Juan Vásquez --- app/assets/javascripts/stories.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/stories.js b/app/assets/javascripts/stories.js index f06b95e6..6a607813 100644 --- a/app/assets/javascripts/stories.js +++ b/app/assets/javascripts/stories.js @@ -50,13 +50,13 @@ document.addEventListener("DOMContentLoaded", () => { element.addEventListener("click", function (event) { if (isDirty) { const confirmLeave = confirm("You have unsaved changes. Are you sure you want to go back?"); - if (!confirmLeave) { - // Prevent navigation if the user chooses not to leave - event.preventDefault(); - } else { + if (confirmLeave) { // Optionally, reset isDirty if leaving isDirty = false; addBeforeUnloadEventListener(isDirty) + } else { + // Prevent navigation if the user chooses not to leave + event.preventDefault(); } } }) From d6677945bce18f47871ed84fc8565b18d478bb1c Mon Sep 17 00:00:00 2001 From: "G. Torres" Date: Fri, 5 Sep 2025 20:01:45 -0400 Subject: [PATCH 6/7] Update app/assets/javascripts/stories.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Juan Vásquez --- app/assets/javascripts/stories.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/stories.js b/app/assets/javascripts/stories.js index 6a607813..3fdcc5ba 100644 --- a/app/assets/javascripts/stories.js +++ b/app/assets/javascripts/stories.js @@ -71,7 +71,7 @@ function addBeforeUnloadEventListener(isDirty) { } } -function warnUserifUnsavedEdits(event) { +function warnUserIfUnsavedEdits(event) { event.preventDefault(); event.returnValue = ''; } From bf1c7350ebebc810d765c769fdffeae19f3e0aac Mon Sep 17 00:00:00 2001 From: Juan Vasquez Date: Tue, 16 Sep 2025 09:03:16 -0600 Subject: [PATCH 7/7] Fix method name --- app/assets/javascripts/stories.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/stories.js b/app/assets/javascripts/stories.js index 3fdcc5ba..5d4ecd85 100644 --- a/app/assets/javascripts/stories.js +++ b/app/assets/javascripts/stories.js @@ -65,9 +65,9 @@ document.addEventListener("DOMContentLoaded", () => { function addBeforeUnloadEventListener(isDirty) { if (isDirty) { - window.addEventListener("beforeunload", warnUserifUnsavedEdits); + window.addEventListener("beforeunload", warnUserIfUnsavedEdits); } else { - window.removeEventListener("beforeunload", warnUserifUnsavedEdits); + window.removeEventListener("beforeunload", warnUserIfUnsavedEdits); } }