update cancel and reset behavior

This commit is contained in:
David Kennedy 2025-02-25 15:48:14 -05:00
parent ee6a43dd48
commit fa588e5e35
No known key found for this signature in database
GPG key ID: 6528A5386E66B96B
3 changed files with 120 additions and 13 deletions

View file

@ -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() {

View file

@ -62,13 +62,13 @@
<div class="grid-row grid-gap-2 flex-end">
<div class="tablet:grid-col-6">
{% 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 %}
</div>
<div class="tablet:grid-col-6">
{% 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 %}
</div>
@ -164,13 +164,13 @@
<tr class="edit-row display-none">
<td>
{% 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 %}
</td>
<td>
{% 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 %}
</td>
@ -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 <span>, 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 %}
</div>
<div class="tablet:grid-col-6">
{% 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 %}
</div>

View file

@ -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