mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-13 21:19:42 +02:00
Merge pull request #1672 from cisagov/rh/1500-domain-req-alt-req-same
ISSUE #1500: Add "Check Availability" functionality for alternative domains
This commit is contained in:
commit
057770c584
3 changed files with 109 additions and 23 deletions
|
@ -130,7 +130,7 @@ function inlineToast(el, id, style, msg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _checkDomainAvailability(el) {
|
function checkDomainAvailability(el) {
|
||||||
const callback = (response) => {
|
const callback = (response) => {
|
||||||
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);
|
||||||
|
@ -154,9 +154,6 @@ function _checkDomainAvailability(el) {
|
||||||
fetchJSON(`available/?domain=${el.value}`, callback);
|
fetchJSON(`available/?domain=${el.value}`, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Call the API to see if the domain is good. */
|
|
||||||
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) {
|
||||||
el.classList.remove('usa-input--success');
|
el.classList.remove('usa-input--success');
|
||||||
|
@ -206,13 +203,33 @@ function handleInputValidation(e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** On button click, handles running any associated validators. */
|
/** On button click, handles running any associated validators. */
|
||||||
function handleValidationClick(e) {
|
function validateFieldInput(e) {
|
||||||
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);
|
||||||
|
removeFormErrors(input, true);
|
||||||
runValidators(input);
|
runValidators(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function validateFormsetInputs(e, availabilityButton) {
|
||||||
|
|
||||||
|
// Collect input IDs from the repeatable forms
|
||||||
|
let inputs = Array.from(document.querySelectorAll('.repeatable-form input'))
|
||||||
|
|
||||||
|
// Run validators for each input
|
||||||
|
inputs.forEach(input => {
|
||||||
|
runValidators(input);
|
||||||
|
removeFormErrors(input, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set the validate-for attribute on the button with the collected input IDs
|
||||||
|
// Not needed for functionality but nice for accessibility
|
||||||
|
inputs = inputs.map(input => input.id).join(', ');
|
||||||
|
availabilityButton.setAttribute('validate-for', inputs);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// <<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>>
|
// <<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>>
|
||||||
// Initialization code.
|
// Initialization code.
|
||||||
|
|
||||||
|
@ -232,14 +249,64 @@ function handleValidationClick(e) {
|
||||||
for(const input of needsValidation) {
|
for(const input of needsValidation) {
|
||||||
input.addEventListener('input', handleInputValidation);
|
input.addEventListener('input', handleInputValidation);
|
||||||
}
|
}
|
||||||
|
const alternativeDomainsAvailability = document.getElementById('validate-alt-domains-availability');
|
||||||
const activatesValidation = document.querySelectorAll('[validate-for]');
|
const activatesValidation = document.querySelectorAll('[validate-for]');
|
||||||
|
|
||||||
for(const button of activatesValidation) {
|
for(const button of activatesValidation) {
|
||||||
button.addEventListener('click', handleValidationClick);
|
// Adds multi-field validation for alternative domains
|
||||||
|
if (button === alternativeDomainsAvailability) {
|
||||||
|
button.addEventListener('click', (e) => {
|
||||||
|
validateFormsetInputs(e, alternativeDomainsAvailability)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
button.addEventListener('click', validateFieldInput);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete method for formsets that diff in the view and delete in the model (Nameservers, DS Data)
|
* Removes form errors surrounding a form input
|
||||||
|
*/
|
||||||
|
function removeFormErrors(input, removeStaleAlerts=false){
|
||||||
|
// Remove error message
|
||||||
|
let errorMessage = document.getElementById(`${input.id}__error-message`);
|
||||||
|
if (errorMessage) {
|
||||||
|
errorMessage.remove();
|
||||||
|
}else{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove error classes
|
||||||
|
if (input.classList.contains('usa-input--error')) {
|
||||||
|
input.classList.remove('usa-input--error');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the form label
|
||||||
|
let label = document.querySelector(`label[for="${input.id}"]`);
|
||||||
|
if (label) {
|
||||||
|
label.classList.remove('usa-label--error');
|
||||||
|
|
||||||
|
// Remove error classes from parent div
|
||||||
|
let parentDiv = label.parentElement;
|
||||||
|
if (parentDiv) {
|
||||||
|
parentDiv.classList.remove('usa-form-group--error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removeStaleAlerts){
|
||||||
|
let staleAlerts = document.querySelectorAll(".usa-alert--error")
|
||||||
|
for (let alert of staleAlerts){
|
||||||
|
// Don't remove the error associated with the input
|
||||||
|
if (alert.id !== `${input.id}--toast`) {
|
||||||
|
alert.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the namerservers and DS data forms delete buttons
|
||||||
|
* We will call this on the forms init, and also every time we add a form
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function removeForm(e, formLabel, isNameserversForm, addButton, formIdentifier){
|
function removeForm(e, formLabel, isNameserversForm, addButton, formIdentifier){
|
||||||
|
@ -460,6 +527,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;
|
||||||
|
@ -472,6 +540,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`);
|
||||||
|
|
||||||
|
@ -554,6 +624,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
|
||||||
|
|
||||||
|
@ -566,22 +637,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
|
||||||
|
@ -592,6 +666,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
|
||||||
|
|
|
@ -420,7 +420,7 @@ class AlternativeDomainForm(RegistrarForm):
|
||||||
|
|
||||||
alternative_domain = forms.CharField(
|
alternative_domain = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label="",
|
label="Alternative domain",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -50,14 +50,14 @@
|
||||||
<button
|
<button
|
||||||
id="check-availability-button"
|
id="check-availability-button"
|
||||||
type="button"
|
type="button"
|
||||||
class="usa-button"
|
class="usa-button usa-button--outline"
|
||||||
validate-for="{{ forms.0.requested_domain.auto_id }}"
|
validate-for="{{ forms.0.requested_domain.auto_id }}"
|
||||||
>Check availability</button>
|
>Check availability</button>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
{{ 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,23 +66,34 @@
|
||||||
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="submit" 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>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</fieldset>
|
<div class="margin-bottom-3">
|
||||||
|
<button
|
||||||
|
id="validate-alt-domains-availability"
|
||||||
|
type="button"
|
||||||
|
class="usa-button usa-button--outline"
|
||||||
|
validate-for="{{ forms.1.requested_domain.auto_id }}"
|
||||||
|
>Check availability</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<p class="margin-top-05">If you’re not sure this is the domain you want, that’s ok. You can change the domain later. </p>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue