diff --git a/app/javascript/controllers/evaluation_form_controller.js b/app/javascript/controllers/evaluation_form_controller.js
index 1d9fc0e0..104c6dfd 100644
--- a/app/javascript/controllers/evaluation_form_controller.js
+++ b/app/javascript/controllers/evaluation_form_controller.js
@@ -32,7 +32,9 @@ export default class extends Controller {
// Opens all accordions, remove existing points/weights, update max points/weights values
updateMaxPoints(e) {
- const form = e.target.closest('form[data-controller="evaluation-form"]');
+ const form = e.target.closest(
+ 'form[data-controller="evaluation-form modal"]'
+ );
const pointsWeights = form.querySelectorAll(".points-or-weight");
const weightedScale = e.target.value === "true";
diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js
index 82491138..dd866ed6 100644
--- a/app/javascript/controllers/index.js
+++ b/app/javascript/controllers/index.js
@@ -8,7 +8,10 @@ import DeleteEvaluatorModalController from "./delete_evaluator_modal_controller"
application.register("delete-evaluator-modal", DeleteEvaluatorModalController);
import UnassignEvaluatorSubmissionModalController from "./unassign_evaluator_submission_modal_controller";
-application.register("unassign-evaluator-submission-modal", UnassignEvaluatorSubmissionModalController);
+application.register(
+ "unassign-evaluator-submission-modal",
+ UnassignEvaluatorSubmissionModalController
+);
import EvaluationCriteriaController from "./evaluation_criteria_controller";
application.register("evaluation-criteria", EvaluationCriteriaController);
@@ -17,4 +20,7 @@ import EvaluationFormController from "./evaluation_form_controller";
application.register("evaluation-form", EvaluationFormController);
import HotdogController from "./hotdog_controller";
-application.register("hotdog", HotdogController);
\ No newline at end of file
+application.register("hotdog", HotdogController);
+
+import ModalController from "./modal_controller";
+application.register("modal", ModalController);
diff --git a/app/javascript/controllers/modal_controller.js b/app/javascript/controllers/modal_controller.js
new file mode 100644
index 00000000..c2eb5b9d
--- /dev/null
+++ b/app/javascript/controllers/modal_controller.js
@@ -0,0 +1,87 @@
+import { Controller } from "@hotwired/stimulus";
+
+export default class extends Controller {
+ static targets = ["modal"];
+ static values = {
+ modalId: String,
+ };
+
+ open(event) {
+ const modalId = event.currentTarget.dataset.modalTargetId;
+ const modal = this.modalTargets.find((modal) => modal.id === modalId);
+
+ this.openEvent = event;
+
+ event.preventDefault();
+
+ if (modal) {
+ modal.showModal();
+ } else {
+ console.warn(`Modal with ID '${modalId}' not found.`);
+ }
+ }
+
+ confirm(event) {
+ const modal = this._getModal(event);
+
+ if (modal) {
+ const confirmRedirect = modal.dataset.modalConfirmRedirect;
+ const confirmAction = modal.dataset.modalConfirmAction;
+
+ if (confirmRedirect) {
+ window.location.href = confirmRedirect;
+ } else if (confirmAction) {
+ this.invokeAction(confirmAction);
+ modal.close();
+ } else {
+ modal.close();
+ return true;
+ }
+ }
+ }
+
+ cancel(event) {
+ const modal = this._getModal(event);
+
+ if (modal) {
+ const cancelRedirect = modal.dataset.modalCancelRedirect;
+ const cancelAction = modal.dataset.modalCancelAction;
+
+ if (cancelRedirect) {
+ window.location.href = cancelRedirect;
+ } else if (cancelAction) {
+ this.invokeAction(cancelAction);
+ modal.close();
+ } else {
+ modal.close();
+ return false;
+ }
+ }
+ }
+
+ _getModal(event) {
+ return event.currentTarget.closest("dialog");
+ }
+
+ invokeAction(actionName) {
+ const [controllerName, action] = actionName.split("#");
+ const controllerElement = document.querySelector(
+ `[data-controller~="${controllerName}"]`
+ );
+
+ if (!controllerElement) {
+ console.warn(`Controller element for ${controllerName} not found.`);
+ }
+
+ const controller = this.application.getControllerForElementAndIdentifier(
+ controllerElement,
+ controllerName
+ );
+
+ if (controller && typeof controller[action] === "function") {
+ controller[action](this.openEvent);
+ } else {
+ console.warn(`Action ${actionName} not found on ${controllerName}`);
+ }
+ }
+}
diff --git a/app/views/evaluation_forms/_evaluation_criterion_fields.html.erb b/app/views/evaluation_forms/_evaluation_criterion_fields.html.erb
index 9250bafc..63302709 100644
--- a/app/views/evaluation_forms/_evaluation_criterion_fields.html.erb
+++ b/app/views/evaluation_forms/_evaluation_criterion_fields.html.erb
@@ -183,7 +183,7 @@
<% if !form_disabled %>
- <%= button_tag type: "button", id: criteria_field_id(f, "delete_criteria", is_template), class: "delete-criteria-button usa-button usa-button--unstyled", title: "Delete criteria", data: {action: "click->evaluation-criteria#removeCriteria"} do %>
+ <%= button_tag type: "button", id: criteria_field_id(f, "delete_criteria", is_template), class: "delete-criteria-button usa-button usa-button--unstyled", title: "Delete criteria", data: {"action": "modal#open", "modal-target-id": "remove-criteria"} do %>
Remove criteria
<% end %>
diff --git a/app/views/evaluation_forms/_form.html.erb b/app/views/evaluation_forms/_form.html.erb
index 6c327f2a..8a36bb7a 100644
--- a/app/views/evaluation_forms/_form.html.erb
+++ b/app/views/evaluation_forms/_form.html.erb
@@ -1,4 +1,4 @@
-<%= form_with(model: evaluation_form, data: { controller: "evaluation-form" }) do |form| %>
+<%= form_with(model: evaluation_form, data: { controller: "evaluation-form modal" }) do |form| %>
<% if evaluation_form.errors.any? %>
<%= pluralize(evaluation_form.errors.count, "error") %> prohibited this evaluation form from being saved:
@@ -200,7 +200,25 @@
Save
- <%= link_to "Cancel", evaluation_forms_path %>
-
+ <%= link_to "Cancel", "#", data: {"action": "modal#open", "modal-target-id": "cancel"} %>
+
-<% end %>
\ No newline at end of file
+
+ <%= render "modals/confirmation",
+ id: "cancel",
+ heading: "Are you sure you want to cancel?",
+ description: "Your evaluation form will not be saved and any entered information will be lost.",
+ confirm_text: "Yes",
+ cancel_text: "Close",
+ confirm_redirect: evaluation_forms_path
+ %>
+
+ <%= render "modals/confirmation",
+ id: "remove-criteria",
+ heading: "Are you sure you want to remove this criteria?",
+ description: "",
+ confirm_text: "Yes",
+ cancel_text: "No",
+ confirm_action: "evaluation-criteria#removeCriteria"
+ %>
+<% end %>
diff --git a/app/views/modals/_confirmation.erb b/app/views/modals/_confirmation.erb
new file mode 100644
index 00000000..0bf45f02
--- /dev/null
+++ b/app/views/modals/_confirmation.erb
@@ -0,0 +1,28 @@
+
\ No newline at end of file
diff --git a/spec/system/evaluation_form_spec.rb b/spec/system/evaluation_form_spec.rb
index 03ec9b67..6cc66f87 100644
--- a/spec/system/evaluation_form_spec.rb
+++ b/spec/system/evaluation_form_spec.rb
@@ -16,6 +16,45 @@
expect(page).to(be_axe_clean)
end
+ it "shows a confirmation modal when clicking the cancel button" do
+ visit new_evaluation_form_path
+
+ click_link_or_button "Cancel"
+
+ assert_selector 'dialog#cancel', visible: true
+
+ expect(page).to(be_axe_clean)
+ end
+
+ it "redirects to evaluation form path when clicking yes in cancel modal" do
+ visit new_evaluation_form_path
+
+ click_link_or_button "Cancel"
+
+ assert_selector 'dialog#cancel', visible: true
+
+ within 'dialog#cancel' do
+ click_link_or_button 'Yes'
+ end
+
+ assert_current_path evaluation_forms_path
+ end
+
+ it "closes the cancel modal and does nothing if you click close" do
+ visit new_evaluation_form_path
+
+ click_link_or_button "Cancel"
+
+ assert_selector 'dialog#cancel', visible: true
+
+ within 'dialog#cancel' do
+ click_link_or_button 'Close'
+ end
+
+ assert_no_selector 'dialog#cancel', visible: true
+ assert_current_path new_evaluation_form_path
+ end
+
it 'allows creation of a valid form with all 3 criteria scoring types' do
visit new_evaluation_form_path
@@ -291,6 +330,45 @@
expect(page).to(be_axe_clean)
end
+ it "shows a confirmation modal when clicking the cancel button" do
+ visit edit_evaluation_form_path(evaluation_form)
+
+ click_link_or_button "Cancel"
+
+ assert_selector 'dialog#cancel', visible: true
+
+ expect(page).to(be_axe_clean)
+ end
+
+ it "redirects to evaluation form path when clicking yes in cancel modal" do
+ visit edit_evaluation_form_path(evaluation_form)
+
+ click_link_or_button "Cancel"
+
+ assert_selector 'dialog#cancel', visible: true
+
+ within 'dialog#cancel' do
+ click_link_or_button 'Yes'
+ end
+
+ assert_current_path evaluation_forms_path
+ end
+
+ it "closes the cancel modal and does nothing if you click close" do
+ visit edit_evaluation_form_path(evaluation_form)
+
+ click_link_or_button "Cancel"
+
+ assert_selector 'dialog#cancel', visible: true
+
+ within 'dialog#cancel' do
+ click_link_or_button 'Close'
+ end
+
+ assert_no_selector 'dialog#cancel', visible: true
+ assert_current_path edit_evaluation_form_path(evaluation_form)
+ end
+
it 'allows editing of an existing form values' do
visit edit_evaluation_form_path(evaluation_form)
@@ -377,7 +455,7 @@
visit edit_evaluation_form_path(closed_evaluation_form)
# Add expectation in spec to satisfy rubocop
- expect(page).to have_css("form[data-controller='evaluation-form']")
+ expect(page).to have_css("form[data-controller='evaluation-form modal']")
check_all_non_hidden_inputs_disabled_except_end_date
end
end
@@ -519,6 +597,12 @@ def add_criterion
def remove_criterion(index)
click_link_or_button "evaluation_form_evaluation_criteria_attributes_#{index}_delete_criteria"
+
+ assert_selector 'dialog#remove-criteria', visible: true
+
+ within 'dialog#remove-criteria' do
+ click_link_or_button 'Yes'
+ end
end
def toggle_criteria_accordion(index)
@@ -791,7 +875,7 @@ def rebalance_criteria_weights
# Checks that all non hidden or end date fields are disabled
def check_all_non_hidden_inputs_disabled_except_end_date
- within("form[data-controller='evaluation-form']") do
+ within("form[data-controller='evaluation-form modal']") do
all("input:not([type='hidden']), textarea, select").each do |field|
if field[:id] == "evaluation_form_closing_date"
expect(field).not_to be_disabled, "Expected #{field[:id]} to not be disabled"