IMplement add form for alternative domains

This commit is contained in:
Rachid Mrad 2024-01-22 19:36:02 -05:00
parent ae8220587e
commit ac6d46b9f8
No known key found for this signature in database
GPG key ID: EF38E4CEC4A8F3CF
3 changed files with 68 additions and 23 deletions

View file

@ -48,6 +48,7 @@ function createLiveRegion(id) {
/** Announces changes to assistive technology users. */ /** Announces changes to assistive technology users. */
function announce(id, text) { function announce(id, text) {
console.log('announce: ' + text)
let liveRegion = document.getElementById(id + "-live-region"); let liveRegion = document.getElementById(id + "-live-region");
if (!liveRegion) liveRegion = createLiveRegion(id); if (!liveRegion) liveRegion = createLiveRegion(id);
liveRegion.innerHTML = text; liveRegion.innerHTML = text;
@ -86,6 +87,7 @@ function fetchJSON(endpoint, callback, url="/api/v1/") {
/** Modifies CSS and HTML when an input is valid/invalid. */ /** Modifies CSS and HTML when an input is valid/invalid. */
function toggleInputValidity(el, valid, msg=DEFAULT_ERROR) { function toggleInputValidity(el, valid, msg=DEFAULT_ERROR) {
console.log('toggleInputValidity: ' + valid)
if (valid) { if (valid) {
el.setCustomValidity(""); el.setCustomValidity("");
el.removeAttribute("aria-invalid"); el.removeAttribute("aria-invalid");
@ -100,6 +102,7 @@ function toggleInputValidity(el, valid, msg=DEFAULT_ERROR) {
/** Display (or hide) a message beneath an element. */ /** Display (or hide) a message beneath an element. */
function inlineToast(el, id, style, msg) { function inlineToast(el, id, style, msg) {
console.log('inine toast creates alerts')
if (!el.id && !id) { if (!el.id && !id) {
console.error("Elements must have an `id` to show an inline toast."); console.error("Elements must have an `id` to show an inline toast.");
return; return;
@ -130,8 +133,10 @@ function inlineToast(el, id, style, msg) {
} }
} }
function _checkDomainAvailability(el) { function checkDomainAvailability(el) {
console.log('checkDomainAvailability: ' + el.value)
const callback = (response) => { const callback = (response) => {
console.log('inside callback')
toggleInputValidity(el, (response && response.available), msg=response.message); toggleInputValidity(el, (response && response.available), msg=response.message);
announce(el.id, response.message); announce(el.id, response.message);
@ -142,6 +147,7 @@ function _checkDomainAvailability(el) {
// use of `parentElement` due to .gov inputs being wrapped in www/.gov decoration // use of `parentElement` due to .gov inputs being wrapped in www/.gov decoration
inlineToast(el.parentElement, el.id, SUCCESS, response.message); inlineToast(el.parentElement, el.id, SUCCESS, response.message);
} else if (ignore_blank && response.code == "required"){ } else if (ignore_blank && response.code == "required"){
console.log('ignore_blank && response.code == "required"')
// Visually remove the error // Visually remove the error
error = "usa-input--error" error = "usa-input--error"
if (el.classList.contains(error)){ if (el.classList.contains(error)){
@ -155,7 +161,7 @@ function _checkDomainAvailability(el) {
} }
/** Call the API to see if the domain is good. */ /** Call the API to see if the domain is good. */
const checkDomainAvailability = debounce(_checkDomainAvailability); // const checkDomainAvailability = debounce(_checkDomainAvailability);
/** Hides the toast message and clears the aira live region. */ /** Hides the toast message and clears the aira live region. */
function clearDomainAvailability(el) { function clearDomainAvailability(el) {
@ -167,6 +173,7 @@ function clearDomainAvailability(el) {
/** Runs all the validators associated with this element. */ /** Runs all the validators associated with this element. */
function runValidators(el) { function runValidators(el) {
console.log(el.getAttribute("id"))
const attribute = el.getAttribute("validate") || ""; const attribute = el.getAttribute("validate") || "";
if (!attribute.length) return; if (!attribute.length) return;
const validators = attribute.split(" "); const validators = attribute.split(" ");
@ -207,12 +214,37 @@ function handleInputValidation(e) {
/** On button click, handles running any associated validators. */ /** On button click, handles running any associated validators. */
function handleValidationClick(e) { function handleValidationClick(e) {
console.log('validating dotgov domain')
const attribute = e.target.getAttribute("validate-for") || ""; const attribute = e.target.getAttribute("validate-for") || "";
if (!attribute.length) return; if (!attribute.length) return;
const input = document.getElementById(attribute);
const input = document.getElementById(attribute); // You might need to define 'attribute'
runValidators(input); runValidators(input);
} }
function handleFormsetValidationClick(e) {
// Check availability for alternative domains
console.log('validating alternative domains')
const alternativeDomainsAvailability = document.getElementById('check-availability-for-alternative-domains');
// Collect input IDs from the repeatable forms
let inputIds = Array.from(document.querySelectorAll('.repeatable-form input')).map(input => input.id);
// Run validators for each input
inputIds.forEach(inputId => {
const input = document.getElementById(inputId);
runValidators(input);
});
// Set the validate-for attribute on the button with the collected input IDs
// Not needed for functionality but nice for accessibility
alternativeDomainsAvailability.setAttribute('validate-for', inputIds.join(', '));
}
// <<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>> // <<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>>
// Initialization code. // Initialization code.
@ -232,10 +264,17 @@ function handleValidationClick(e) {
for(const input of needsValidation) { for(const input of needsValidation) {
input.addEventListener('input', handleInputValidation); input.addEventListener('input', handleInputValidation);
} }
const dotgovDomainsAvailability = document.getElementById('check-availability-for-dotgov-domain');
const alternativeDomainsAvailability = document.getElementById('check-availability-for-alternative-domains');
const activatesValidation = document.querySelectorAll('[validate-for]'); const activatesValidation = document.querySelectorAll('[validate-for]');
for(const button of activatesValidation) { for(const button of activatesValidation) {
if (alternativeDomainsAvailability) {
alternativeDomainsAvailability.addEventListener('click', handleFormsetValidationClick);
dotgovDomainsAvailability.addEventListener('click', handleValidationClick);
} else {
button.addEventListener('click', handleValidationClick); button.addEventListener('click', handleValidationClick);
} }
}
})(); })();
/** /**
@ -453,6 +492,7 @@ function hideDeletedForms() {
let isNameserversForm = document.querySelector(".nameservers-form"); let isNameserversForm = document.querySelector(".nameservers-form");
let isOtherContactsForm = document.querySelector(".other-contacts-form"); let isOtherContactsForm = document.querySelector(".other-contacts-form");
let isDsDataForm = document.querySelector(".ds-data-form"); let isDsDataForm = document.querySelector(".ds-data-form");
let isDotgovDomain = document.querySelector(".dotgov-domain-form");
// The Nameservers formset features 2 required and 11 optionals // The Nameservers formset features 2 required and 11 optionals
if (isNameserversForm) { if (isNameserversForm) {
cloneIndex = 2; cloneIndex = 2;
@ -465,6 +505,8 @@ function hideDeletedForms() {
formLabel = "Organization contact"; formLabel = "Organization contact";
container = document.querySelector("#other-employees"); container = document.querySelector("#other-employees");
formIdentifier = "other_contacts" formIdentifier = "other_contacts"
} else if (isDotgovDomain) {
formIdentifier = "dotgov_domain"
} }
let totalForms = document.querySelector(`#id_${formIdentifier}-TOTAL_FORMS`); let totalForms = document.querySelector(`#id_${formIdentifier}-TOTAL_FORMS`);
@ -539,6 +581,7 @@ function hideDeletedForms() {
// Reset the values of each input to blank // Reset the values of each input to blank
inputs.forEach((input) => { inputs.forEach((input) => {
input.classList.remove("usa-input--error"); input.classList.remove("usa-input--error");
input.classList.remove("usa-input--success");
if (input.type === "text" || input.type === "number" || input.type === "password" || input.type === "email" || input.type === "tel") { if (input.type === "text" || input.type === "number" || input.type === "password" || input.type === "email" || input.type === "tel") {
input.value = ""; // Set the value to an empty string input.value = ""; // Set the value to an empty string
@ -551,22 +594,25 @@ function hideDeletedForms() {
let selects = newForm.querySelectorAll("select"); let selects = newForm.querySelectorAll("select");
selects.forEach((select) => { selects.forEach((select) => {
select.classList.remove("usa-input--error"); select.classList.remove("usa-input--error");
select.classList.remove("usa-input--success");
select.selectedIndex = 0; // Set the value to an empty string select.selectedIndex = 0; // Set the value to an empty string
}); });
let labels = newForm.querySelectorAll("label"); let labels = newForm.querySelectorAll("label");
labels.forEach((label) => { labels.forEach((label) => {
label.classList.remove("usa-label--error"); label.classList.remove("usa-label--error");
label.classList.remove("usa-label--success");
}); });
let usaFormGroups = newForm.querySelectorAll(".usa-form-group"); let usaFormGroups = newForm.querySelectorAll(".usa-form-group");
usaFormGroups.forEach((usaFormGroup) => { usaFormGroups.forEach((usaFormGroup) => {
usaFormGroup.classList.remove("usa-form-group--error"); usaFormGroup.classList.remove("usa-form-group--error");
usaFormGroup.classList.remove("usa-form-group--success");
}); });
// Remove any existing error messages // Remove any existing error and success messages
let usaErrorMessages = newForm.querySelectorAll(".usa-error-message"); let usaMessages = newForm.querySelectorAll(".usa-error-message, .usa-alert");
usaErrorMessages.forEach((usaErrorMessage) => { usaMessages.forEach((usaErrorMessage) => {
let parentDiv = usaErrorMessage.closest('div'); let parentDiv = usaErrorMessage.closest('div');
if (parentDiv) { if (parentDiv) {
parentDiv.remove(); // Remove the parent div if it exists parentDiv.remove(); // Remove the parent div if it exists
@ -577,6 +623,7 @@ function hideDeletedForms() {
// Attach click event listener on the delete buttons of the new form // Attach click event listener on the delete buttons of the new form
let newDeleteButton = newForm.querySelector(".delete-record"); let newDeleteButton = newForm.querySelector(".delete-record");
if (newDeleteButton)
prepareNewDeleteButton(newDeleteButton, formLabel); prepareNewDeleteButton(newDeleteButton, formLabel);
// Disable the add more button if we have 13 forms // Disable the add more button if we have 13 forms

View file

@ -420,7 +420,7 @@ class AlternativeDomainForm(RegistrarForm):
alternative_domain = forms.CharField( alternative_domain = forms.CharField(
required=False, required=False,
label="", label="Alternative domain",
) )

View file

@ -48,7 +48,7 @@
{% endwith %} {% endwith %}
{% endwith %} {% endwith %}
<button <button
id="check-availability-button" id="check-availability-for-dotgov-domain"
type="button" type="button"
class="usa-button usa-button--outline" class="usa-button usa-button--outline"
validate-for="{{ forms.0.requested_domain.auto_id }}" validate-for="{{ forms.0.requested_domain.auto_id }}"
@ -57,7 +57,7 @@
{{ forms.1.management_form }} {{ forms.1.management_form }}
<fieldset class="usa-fieldset margin-top-1"> <fieldset class="usa-fieldset margin-top-1 dotgov-domain-form" id="form-container">
<legend> <legend>
<h2>Alternative domains (optional)</h2> <h2>Alternative domains (optional)</h2>
</legend> </legend>
@ -66,18 +66,17 @@
you your first choice?</p> you your first choice?</p>
{% with attr_aria_describedby="alt_domain_instructions" %} {% with attr_aria_describedby="alt_domain_instructions" %}
{# attr_validate / validate="domain" invokes code in get-gov.js #} {# Will probably want to remove blank-ok and do related cleanup when we implement delete #}
{# attr_auto_validate likewise triggers behavior in get-gov.js #} {% with attr_validate="domain" append_gov=True add_label_class="usa-sr-only" add_class="blank-ok alternate-domain-input" %}
{% with append_gov=True attr_validate="domain" attr_auto_validate=True %}
{% with add_class="blank-ok alternate-domain-input" %}
{% for form in forms.1 %} {% for form in forms.1 %}
<div class="repeatable-form">
{% input_with_errors form.alternative_domain %} {% input_with_errors form.alternative_domain %}
</div>
{% endfor %} {% endfor %}
{% endwith %} {% endwith %}
{% endwith %} {% endwith %}
{% endwith %}
<button type="button" name="submit_button" value="save" class="usa-button usa-button--unstyled"> <button type="button" value="save" class="usa-button usa-button--unstyled" id="add-form">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24" height="24"> <svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24" height="24">
<use xlink:href="{%static 'img/sprite.svg'%}#add_circle"></use> <use xlink:href="{%static 'img/sprite.svg'%}#add_circle"></use>
</svg><span class="margin-left-05">Add another alternative</span> </svg><span class="margin-left-05">Add another alternative</span>
@ -86,10 +85,9 @@
<br> <br>
<button <button
id="check-availability-button" id="check-availability-for-alternative-domains"
type="submit" type="button"
class="usa-button usa-button--outline" class="usa-button usa-button--outline"
validate-for="{{ forms.0.requested_domain.auto_id }}"
>Check availability</button> >Check availability</button>
<p>If youre not sure this is the domain you want, thats ok. You can change the domain later. </p> <p>If youre not sure this is the domain you want, thats ok. You can change the domain later. </p>