From fa588e5e352bf774507c2a6e711b1770eb561bf7 Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Tue, 25 Feb 2025 15:48:14 -0500 Subject: [PATCH] update cancel and reset behavior --- .../assets/src/js/getgov/form-nameservers.js | 115 +++++++++++++++++- .../templates/domain_nameservers.html | 14 +-- src/registrar/templatetags/field_helpers.py | 4 + 3 files changed, 120 insertions(+), 13 deletions(-) diff --git a/src/registrar/assets/src/js/getgov/form-nameservers.js b/src/registrar/assets/src/js/getgov/form-nameservers.js index 6b425625a..fc67d3edc 100644 --- a/src/registrar/assets/src/js/getgov/form-nameservers.js +++ b/src/registrar/assets/src/js/getgov/form-nameservers.js @@ -4,6 +4,7 @@ export class NameserverForm { constructor() { this.addNameserverButton = document.getElementById('nameserver-add-form'); this.nameserversForm = document.querySelector('.nameservers-form'); + this.formChanged = false; // Bind event handlers to maintain 'this' context this.handleAddFormClick = this.handleAddFormClick.bind(this); @@ -28,6 +29,20 @@ export class NameserverForm { showElement(this.nameserversForm); hideElement(this.addNameserverButton); } + + // handle display of table view errors + // if error exists in an edit-row, make that row show, and readonly row hide + const formTable = document.querySelector('.usa-table') + if (formTable) { + const editRows = formTable.querySelectorAll('.edit-row'); + editRows.forEach(editRow => { + if (editRow.querySelector('.usa-input--error')) { + const readOnlyRow = editRow.previousElementSibling; + showElement(editRow); + hideElement(readOnlyRow); + } + }) + } } initializeEventListeners() { @@ -42,6 +57,14 @@ export class NameserverForm { cancelButtons.forEach(cancelButton => { cancelButton.addEventListener('click', this.handleCancelClick); }); + + const textInputs = document.querySelectorAll("input[type='text']"); + textInputs.forEach(input => { + input.addEventListener("input", function() { + this.formChanged = true; + console.log("Form changed"); + }) + }) } handleAddFormClick(event) { @@ -53,37 +76,117 @@ export class NameserverForm { let editButton = event.target; let readOnlyRow = editButton.closest('tr'); // Find the closest row let editRow = readOnlyRow.nextElementSibling; // Get the next row - if (!editRow || !readOnlyRow) { console.warn("Expected DOM element but did not find it"); return; } - // Check if any other edit row is currently visible and hide it document.querySelectorAll('tr.edit-row:not(.display-none)').forEach(openEditRow => { let correspondingReadOnlyRow = openEditRow.previousElementSibling; hideElement(openEditRow); showElement(correspondingReadOnlyRow); }); - + // hide and show rows as appropriate hideElement(readOnlyRow); showElement(editRow); } + /** + * Handles the click event for the cancel button within the table form. + * + * This method identifies the edit row containing the cancel button and resets + * it to its initial state, restoring the corresponding read-only row. + * + * @param {Event} event - the click event triggered by the cancel button + */ handleCancelClick(event) { + // get the cancel button that was clicked let cancelButton = event.target; - let editRow = cancelButton.closest('tr'); // Find the closest row - let readOnlyRow = editRow.previousElementSibling; // Get the next row + // find the closest table row that contains the cancel button + let editRow = cancelButton.closest('tr'); + if (editRow) { + this.resetEditRowAndFormAndCollapseEditRow(editRow); + } else { + console.warn("Expected DOM element but did not find it"); + } + } + resetEditRowAndFormAndCollapseEditRow(editRow) { + let readOnlyRow = editRow.previousElementSibling; // Get the next row if (!editRow || !readOnlyRow) { console.warn("Expected DOM element but did not find it"); return; } - + // reset the values set in editRow + this.resetInputValuesInRow(editRow); + // copy values from editRow to readOnlyRow + this.copyEditRowToReadonlyRow(editRow, readOnlyRow); + // remove errors from the editRow + this.removeErrorsFromRow(editRow); + // remove errors from the entire form + this.removeFormErrors(); + // reset formChanged + this.resetFormChanged(); + // hide and show rows as appropriate hideElement(editRow); showElement(readOnlyRow); } + resetInputValuesInRow(editRow) { + let textInputs = editRow.querySelectorAll("input[type='text']"); + textInputs.forEach(input => { + input.value = input.dataset.initialValue; + }) + } + + copyEditRowToReadonlyRow(editRow, readOnlyRow) { + let textInputs = editRow.querySelectorAll("input[type='text']"); + let tds = readOnlyRow.querySelectorAll("td"); + + // if server is defined, copy the value to the first td in readOnlyRow + if (textInputs[0] && tds[0]) { + tds[0].innerText = textInputs[0].value; + } + + // if ip is defined, copy the value to the second td in readOnlyRow + if (textInputs[1] && tds[1]) { + tds[1].innerText = textInputs[1].value; + } + } + + removeErrorsFromRow(editRow) { + // remove class 'usa-form-group--error' from divs in editRow + editRow.querySelectorAll("div.usa-form-group--error").forEach(div => { + div.classList.remove("usa-form-group--error"); + }); + + // remove class 'usa-label--error' from labels in editRow + editRow.querySelectorAll("label.usa-label--error").forEach(label => { + label.classList.remove("usa-label--error"); + }); + + // Remove divs whose id ends with '__error-message' (error message divs) + editRow.querySelectorAll("div[id$='__error-message']").forEach(errorDiv => { + errorDiv.remove(); + }); + + // remove class 'usa-input--error' from inputs in editRow + editRow.querySelectorAll("input.usa-input--error").forEach(input => { + input.classList.remove("usa-input--error"); + }); + } + + removeFormErrors() { + let formErrorDiv = document.getElementById("form-errors"); + if (formErrorDiv) { + formErrorDiv.remove(); + } + } + + resetFormChanged() { + this.formChanged = false; + } + } export function initFormNameservers() { diff --git a/src/registrar/templates/domain_nameservers.html b/src/registrar/templates/domain_nameservers.html index 856f4f62c..d547378df 100644 --- a/src/registrar/templates/domain_nameservers.html +++ b/src/registrar/templates/domain_nameservers.html @@ -62,13 +62,13 @@
{% with sublabel_text="Example: ns"|concat:forloop.counter|concat:".example.com" %} - {% with attr_required=True span_for_text=True %} + {% with attr_required=True span_for_text=True add_initial_value_attr=True %} {% input_with_errors form.server %} {% endwith %} {% endwith %}
- {% with attr_required=True label_text=form.ip.label sublabel_text="Example: 86.124.49.54 or 2001:db8::1234:5678" add_group_class="usa-form-group--unstyled-error" add_aria_label="Name server "|concat:forloop.counter|concat:" "|concat:form.ip.label %} + {% with attr_required=True add_initial_value_attr=True label_text=form.ip.label sublabel_text="Example: 86.124.49.54 or 2001:db8::1234:5678" add_group_class="usa-form-group--unstyled-error" add_aria_label="Name server "|concat:forloop.counter|concat:" "|concat:form.ip.label %} {% input_with_errors form.ip %} {% endwith %}
@@ -164,13 +164,13 @@ {% with sublabel_text="Example: ns"|concat:forloop.counter|concat:".example.com" %} - {% with attr_required=True span_for_text=True add_group_class="margin-top-0" %} + {% with attr_required=True add_initial_value_attr=True span_for_text=True add_group_class="margin-top-0" %} {% input_with_errors form.server %} {% endwith %} {% endwith %} - {% with attr_required=True label_text=form.ip.label sublabel_text="Example: 86.124.49.54 or 2001:db8::1234:5678" add_group_class="usa-form-group--unstyled-error" add_aria_label="Name server "|concat:forloop.counter|concat:" "|concat:form.ip.label add_group_class="margin-top-0" %} + {% with attr_required=True add_initial_value_attr=True label_text=form.ip.label sublabel_text="Example: 86.124.49.54 or 2001:db8::1234:5678" add_group_class="usa-form-group--unstyled-error" add_aria_label="Name server "|concat:forloop.counter|concat:" "|concat:form.ip.label add_group_class="margin-top-0" %} {% input_with_errors form.ip %} {% endwith %} @@ -225,18 +225,18 @@ {% with sublabel_text="Example: ns"|concat:forloop.counter|concat:".example.com" %} {% if forloop.counter <= 2 %} {# span_for_text will wrap the copy in s , which we'll use in the JS for this component #} - {% with attr_required=True add_group_class="usa-form-group--unstyled-error" span_for_text=True %} + {% with attr_required=True add_initial_value_attr=True add_group_class="usa-form-group--unstyled-error" span_for_text=True %} {% input_with_errors form.server %} {% endwith %} {% else %} - {% with span_for_text=True %} + {% with span_for_text=True add_initial_value_attr=True %} {% input_with_errors form.server %} {% endwith %} {% endif %} {% endwith %}
- {% with attr_required=True label_text=form.ip.label sublabel_text="Example: 86.124.49.54 or 2001:db8::1234:5678" add_group_class="usa-form-group--unstyled-error" add_aria_label="Name server "|concat:forloop.counter|concat:" "|concat:form.ip.label %} + {% with attr_required=True add_initial_value_attr=True label_text=form.ip.label sublabel_text="Example: 86.124.49.54 or 2001:db8::1234:5678" add_group_class="usa-form-group--unstyled-error" add_aria_label="Name server "|concat:forloop.counter|concat:" "|concat:form.ip.label %} {% input_with_errors form.ip %} {% endwith %}
diff --git a/src/registrar/templatetags/field_helpers.py b/src/registrar/templatetags/field_helpers.py index 28a4d4556..c7389d3e3 100644 --- a/src/registrar/templatetags/field_helpers.py +++ b/src/registrar/templatetags/field_helpers.py @@ -173,6 +173,10 @@ def input_with_errors(context, field=None): # noqa: C901 if aria_labels: context["aria_label"] = " ".join(aria_labels) + # Conditionally add the data-initial-value attribute + if context.get("add_initial_value_attr", False): + attrs["data-initial-value"] = field.initial or "" + # ask Django to give us the widget dict # see Widget.get_context() on # https://docs.djangoproject.com/en/4.1/ref/forms/widgets