diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 1b6e2de6a..e89147b11 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1367,6 +1367,8 @@ class UserDomainRoleAdmin(ListHeaderAdmin, ImportExportModelAdmin): autocomplete_fields = ["user", "domain"] + change_form_template = "django/admin/user_domain_role_change_form.html" + # Fixes a bug where non-superusers are redirected to the main page def delete_view(self, request, object_id, extra_context=None): """Custom delete_view implementation that specifies redirect behaviour""" @@ -1500,7 +1502,7 @@ class DomainInvitationAdmin(BaseInvitationAdmin): autocomplete_fields = ["domain"] - change_form_template = "django/admin/email_clipboard_change_form.html" + change_form_template = "django/admin/domain_invitation_change_form.html" # Select domain invitations to change -> Domain invitations def changelist_view(self, request, extra_context=None): diff --git a/src/registrar/assets/js/uswds-edited.js b/src/registrar/assets/js/uswds-edited.js index f59417b41..9d4dd2e51 100644 --- a/src/registrar/assets/js/uswds-edited.js +++ b/src/registrar/assets/js/uswds-edited.js @@ -29,6 +29,7 @@ * - tooltip dynamic content updated to include nested element (for better sizing control) * - modal exposed to window to be accessible in other js files * - fixed bug in createHeaderButton which added newlines to header button tooltips + * - modified combobox to handle error class */ if ("document" in window.self) { @@ -1213,6 +1214,11 @@ const enhanceComboBox = _comboBoxEl => { input.setAttribute("class", INPUT_CLASS); input.setAttribute("type", "text"); input.setAttribute("role", "combobox"); + // DOTGOV - handle error class for combobox + // Check if 'usa-input--error' exists in selectEl and add it to input if true + if (selectEl.classList.contains('usa-input--error')) { + input.classList.add('usa-input--error'); + } additionalAttributes.forEach(attr => Object.keys(attr).forEach(key => { const value = Sanitizer.escapeHTML`${attr[key]}`; input.setAttribute(key, value); diff --git a/src/registrar/assets/src/js/getgov/combobox.js b/src/registrar/assets/src/js/getgov/combobox.js deleted file mode 100644 index 36b7aa0ad..000000000 --- a/src/registrar/assets/src/js/getgov/combobox.js +++ /dev/null @@ -1,113 +0,0 @@ -import { hideElement, showElement } from './helpers.js'; - -export function loadInitialValuesForComboBoxes() { - var overrideDefaultClearButton = true; - var isTyping = false; - - document.addEventListener('DOMContentLoaded', (event) => { - handleAllComboBoxElements(); - }); - - function handleAllComboBoxElements() { - const comboBoxElements = document.querySelectorAll(".usa-combo-box"); - comboBoxElements.forEach(comboBox => { - const input = comboBox.querySelector("input"); - const select = comboBox.querySelector("select"); - if (!input || !select) { - console.warn("No combobox element found"); - return; - } - // Set the initial value of the combobox - let initialValue = select.getAttribute("data-default-value"); - let clearInputButton = comboBox.querySelector(".usa-combo-box__clear-input"); - if (!clearInputButton) { - console.warn("No clear element found"); - return; - } - - // Override the default clear button behavior such that it no longer clears the input, - // it just resets to the data-initial-value. - // Due to the nature of how uswds works, this is slightly hacky. - // Use a MutationObserver to watch for changes in the dropdown list - const dropdownList = comboBox.querySelector(`#${input.id}--list`); - const observer = new MutationObserver(function(mutations) { - mutations.forEach(function(mutation) { - if (mutation.type === "childList") { - addBlankOption(clearInputButton, dropdownList, initialValue); - } - }); - }); - - // Configure the observer to watch for changes in the dropdown list - const config = { childList: true, subtree: true }; - observer.observe(dropdownList, config); - - // Input event listener to detect typing - input.addEventListener("input", () => { - isTyping = true; - }); - - // Blur event listener to reset typing state - input.addEventListener("blur", () => { - isTyping = false; - }); - - // Hide the reset button when there is nothing to reset. - // Do this once on init, then everytime a change occurs. - updateClearButtonVisibility(select, initialValue, clearInputButton) - select.addEventListener("change", () => { - updateClearButtonVisibility(select, initialValue, clearInputButton) - }); - - // Change the default input behaviour - have it reset to the data default instead - clearInputButton.addEventListener("click", (e) => { - if (overrideDefaultClearButton && initialValue) { - e.preventDefault(); - e.stopPropagation(); - input.click(); - // Find the dropdown option with the desired value - const dropdownOptions = document.querySelectorAll(".usa-combo-box__list-option"); - if (dropdownOptions) { - dropdownOptions.forEach(option => { - if (option.getAttribute("data-value") === initialValue) { - // Simulate a click event on the dropdown option - option.click(); - } - }); - } - } - }); - }); - } - - function updateClearButtonVisibility(select, initialValue, clearInputButton) { - if (select.value === initialValue) { - hideElement(clearInputButton); - }else { - showElement(clearInputButton) - } - } - - function addBlankOption(clearInputButton, dropdownList, initialValue) { - if (dropdownList && !dropdownList.querySelector('[data-value=""]') && !isTyping) { - const blankOption = document.createElement("li"); - blankOption.setAttribute("role", "option"); - blankOption.setAttribute("data-value", ""); - blankOption.classList.add("usa-combo-box__list-option"); - if (!initialValue){ - blankOption.classList.add("usa-combo-box__list-option--selected") - } - blankOption.textContent = "⎯"; - - dropdownList.insertBefore(blankOption, dropdownList.firstChild); - blankOption.addEventListener("click", (e) => { - e.preventDefault(); - e.stopPropagation(); - overrideDefaultClearButton = false; - // Trigger the default clear behavior - clearInputButton.click(); - overrideDefaultClearButton = true; - }); - } - } -} diff --git a/src/registrar/assets/src/js/getgov/main.js b/src/registrar/assets/src/js/getgov/main.js index 6ff402aa4..a077da929 100644 --- a/src/registrar/assets/src/js/getgov/main.js +++ b/src/registrar/assets/src/js/getgov/main.js @@ -3,7 +3,6 @@ import { initDomainValidators } from './domain-validators.js'; import { initFormsetsForms, triggerModalOnDsDataForm, nameserversFormListener } from './formset-forms.js'; import { initializeUrbanizationToggle } from './urbanization.js'; import { userProfileListener, finishUserSetupListener } from './user-profile.js'; -import { loadInitialValuesForComboBoxes } from './combobox.js'; import { handleRequestingEntityFieldset } from './requesting-entity.js'; import { initDomainsTable } from './table-domains.js'; import { initDomainRequestsTable } from './table-domain-requests.js'; @@ -31,8 +30,6 @@ initializeUrbanizationToggle(); userProfileListener(); finishUserSetupListener(); -loadInitialValuesForComboBoxes(); - handleRequestingEntityFieldset(); initDomainsTable(); diff --git a/src/registrar/assets/src/js/getgov/requesting-entity.js b/src/registrar/assets/src/js/getgov/requesting-entity.js index 3bcdcd35c..833eab2f8 100644 --- a/src/registrar/assets/src/js/getgov/requesting-entity.js +++ b/src/registrar/assets/src/js/getgov/requesting-entity.js @@ -9,15 +9,15 @@ export function handleRequestingEntityFieldset() { const formPrefix = "portfolio_requesting_entity"; const radioFieldset = document.getElementById(`id_${formPrefix}-requesting_entity_is_suborganization__fieldset`); const radios = radioFieldset?.querySelectorAll(`input[name="${formPrefix}-requesting_entity_is_suborganization"]`); - const select = document.getElementById(`id_${formPrefix}-sub_organization`); - const selectParent = select?.parentElement; + const input = document.getElementById(`id_${formPrefix}-sub_organization`); + const inputGrandParent = input?.parentElement?.parentElement; + const select = input?.previousElementSibling; const suborgContainer = document.getElementById("suborganization-container"); const suborgDetailsContainer = document.getElementById("suborganization-container__details"); const suborgAddtlInstruction = document.getElementById("suborganization-addtl-instruction"); - const subOrgCreateNewOption = document.getElementById("option-to-add-suborg")?.value; // Make sure all crucial page elements exist before proceeding. // This more or less ensures that we are on the Requesting Entity page, and not elsewhere. - if (!radios || !select || !selectParent || !suborgContainer || !suborgDetailsContainer) return; + if (!radios || !input || !select || !inputGrandParent || !suborgContainer || !suborgDetailsContainer) return; // requestingSuborganization: This just broadly determines if they're requesting a suborg at all // requestingNewSuborganization: This variable determines if the user is trying to *create* a new suborganization or not. @@ -27,8 +27,8 @@ export function handleRequestingEntityFieldset() { function toggleSuborganization(radio=null) { if (radio != null) requestingSuborganization = radio?.checked && radio.value === "True"; requestingSuborganization ? showElement(suborgContainer) : hideElement(suborgContainer); - if (select.options.length == 2) { // --Select-- and other are the only options - hideElement(selectParent); // Hide the select drop down and indicate requesting new suborg + if (select.options.length == 1) { // other is the only option + hideElement(inputGrandParent); // Hide the combo box and indicate requesting new suborg hideElement(suborgAddtlInstruction); // Hide additional instruction related to the list requestingNewSuborganization.value = "True"; } else { @@ -37,11 +37,6 @@ export function handleRequestingEntityFieldset() { requestingNewSuborganization.value === "True" ? showElement(suborgDetailsContainer) : hideElement(suborgDetailsContainer); } - // Add fake "other" option to sub_organization select - if (select && !Array.from(select.options).some(option => option.value === "other")) { - select.add(new Option(subOrgCreateNewOption, "other")); - } - if (requestingNewSuborganization.value === "True") { select.value = "other"; } diff --git a/src/registrar/assets/src/js/getgov/table-edit-member-domains.js b/src/registrar/assets/src/js/getgov/table-edit-member-domains.js index 86aa39c37..4f0b1d610 100644 --- a/src/registrar/assets/src/js/getgov/table-edit-member-domains.js +++ b/src/registrar/assets/src/js/getgov/table-edit-member-domains.js @@ -259,7 +259,7 @@ export class EditMemberDomainsTable extends BaseTable { // Append unassigned domains section if (this.removedDomains.length) { const unassignedHeader = document.createElement('h3'); - unassignedHeader.classList.add('header--body', 'text-primary', 'margin-bottom-1'); + unassignedHeader.classList.add('margin-bottom-1'); unassignedHeader.textContent = 'Unassigned domains'; domainAssignmentSummary.appendChild(unassignedHeader); domainAssignmentSummary.appendChild(unassignedDomainsList); @@ -268,7 +268,7 @@ export class EditMemberDomainsTable extends BaseTable { // Append assigned domains section if (this.addedDomains.length) { const assignedHeader = document.createElement('h3'); - assignedHeader.classList.add('header--body', 'text-primary', 'margin-bottom-1'); + assignedHeader.classList.add('margin-bottom-1'); assignedHeader.textContent = 'Assigned domains'; domainAssignmentSummary.appendChild(assignedHeader); domainAssignmentSummary.appendChild(assignedDomainsList); @@ -276,7 +276,7 @@ export class EditMemberDomainsTable extends BaseTable { // Append total assigned domains section const totalHeader = document.createElement('h3'); - totalHeader.classList.add('header--body', 'text-primary', 'margin-bottom-1'); + totalHeader.classList.add('margin-bottom-1'); totalHeader.textContent = 'Total assigned domains'; domainAssignmentSummary.appendChild(totalHeader); const totalCount = document.createElement('p'); diff --git a/src/registrar/assets/src/js/getgov/table-members.js b/src/registrar/assets/src/js/getgov/table-members.js index 99a7fc652..2e3130a3e 100644 --- a/src/registrar/assets/src/js/getgov/table-members.js +++ b/src/registrar/assets/src/js/getgov/table-members.js @@ -245,7 +245,7 @@ export class MembersTable extends BaseTable { // Only generate HTML if the member has one or more assigned domains if (num_domains > 0) { domainsHTML += "
This member is assigned to ${num_domains} domains:
`; domainsHTML += "+ If you add someone to a domain here, it will trigger emails to the invitee and all managers of the domain when you click "save." If you don't want to trigger those emails, use the User domain roles permissions table instead. +
++ If you add someone to a domain here, it will not trigger any emails. To trigger emails, use the User Domain Role invitations table instead. +
+