uswds edits to combobox, default values for comboboxes, cleanup of combobox js

This commit is contained in:
David Kennedy 2025-01-14 18:03:24 -05:00
parent e2486c127d
commit ea509f63ef
No known key found for this signature in database
GPG key ID: 6528A5386E66B96B
4 changed files with 21 additions and 52 deletions

View file

@ -29,6 +29,8 @@
* - tooltip dynamic content updated to include nested element (for better sizing control) * - tooltip dynamic content updated to include nested element (for better sizing control)
* - modal exposed to window to be accessible in other js files * - modal exposed to window to be accessible in other js files
* - fixed bug in createHeaderButton which added newlines to header button tooltips * - fixed bug in createHeaderButton which added newlines to header button tooltips
* - modified combobox to allow for blank values in list
* - modified aria label for X button in combobox to reflect modified behavior of button
*/ */
if ("document" in window.self) { if ("document" in window.self) {
@ -1218,9 +1220,11 @@ const enhanceComboBox = _comboBoxEl => {
input.setAttribute(key, value); input.setAttribute(key, value);
})); }));
comboBoxEl.insertAdjacentElement("beforeend", input); comboBoxEl.insertAdjacentElement("beforeend", input);
// DOTGOV - modified the aria-label of the clear input button to Reset selection to reflect changed button behavior
// <button type="button" class="${CLEAR_INPUT_BUTTON_CLASS}" aria-label="Clear the select contents">&nbsp;</button>
comboBoxEl.insertAdjacentHTML("beforeend", Sanitizer.escapeHTML` comboBoxEl.insertAdjacentHTML("beforeend", Sanitizer.escapeHTML`
<span class="${CLEAR_INPUT_BUTTON_WRAPPER_CLASS}" tabindex="-1"> <span class="${CLEAR_INPUT_BUTTON_WRAPPER_CLASS}" tabindex="-1">
<button type="button" class="${CLEAR_INPUT_BUTTON_CLASS}" aria-label="Clear the select contents">&nbsp;</button> <button type="button" class="${CLEAR_INPUT_BUTTON_CLASS}" aria-label="Reset selection">&nbsp;</button>
</span> </span>
<span class="${INPUT_BUTTON_SEPARATOR_CLASS}">&nbsp;</span> <span class="${INPUT_BUTTON_SEPARATOR_CLASS}">&nbsp;</span>
<span class="${TOGGLE_LIST_BUTTON_WRAPPER_CLASS}" tabindex="-1"> <span class="${TOGGLE_LIST_BUTTON_WRAPPER_CLASS}" tabindex="-1">
@ -1356,8 +1360,12 @@ const displayList = el => {
for (let i = 0, len = selectEl.options.length; i < len; i += 1) { for (let i = 0, len = selectEl.options.length; i < len; i += 1) {
const optionEl = selectEl.options[i]; const optionEl = selectEl.options[i];
const optionId = `${listOptionBaseId}${options.length}`; const optionId = `${listOptionBaseId}${options.length}`;
if (optionEl.value && (disableFiltering || isPristine || !inputValue || regex.test(optionEl.text))) { // DOTGOV: modified combobox to allow for options with blank value
if (selectEl.value && optionEl.value === selectEl.value) { //if (optionEl.value && (disableFiltering || isPristine || !inputValue || regex.test(optionEl.text))) {
if ((disableFiltering || isPristine || !inputValue || regex.test(optionEl.text))) {
// DOTGOV: modified combobox to allow blank option value selections to be considered selected
//if (selectEl.value && optionEl.value === selectEl.value) {
if (selectEl.value && optionEl.value === selectEl.value || (!selectEl.value && !optionEl.value)) {
selectedItemId = optionId; selectedItemId = optionId;
} }
if (disableFiltering && !firstFoundId && regex.test(optionEl.text)) { if (disableFiltering && !firstFoundId && regex.test(optionEl.text)) {

View file

@ -28,19 +28,6 @@ export function loadInitialValuesForComboBoxes() {
// Override the default clear button behavior such that it no longer clears the input, // Override the default clear button behavior such that it no longer clears the input,
// it just resets to the data-initial-value. // it just resets to the data-initial-value.
// Due to the nature of how uswds works, this is slightly hacky. // 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 event listener to detect typing
input.addEventListener("input", () => { input.addEventListener("input", () => {
@ -87,27 +74,4 @@ export function loadInitialValuesForComboBoxes() {
showElement(clearInputButton) 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;
});
}
}
} }

View file

@ -164,6 +164,7 @@ class DomainSuborganizationForm(forms.ModelForm):
sub_organization = forms.ModelChoiceField( sub_organization = forms.ModelChoiceField(
label="Suborganization name", label="Suborganization name",
queryset=Suborganization.objects.none(), queryset=Suborganization.objects.none(),
empty_label="⎯ (No suborganization)",
required=False, required=False,
widget=ComboboxWidget, widget=ComboboxWidget,
) )
@ -468,12 +469,11 @@ class DomainOrgNameAddressForm(forms.ModelForm):
state_territory = forms.ChoiceField( state_territory = forms.ChoiceField(
label="State, territory, or military post", label="State, territory, or military post",
required=True, required=True,
choices=DomainInformation.StateTerritoryChoices.choices, choices=[("", "--Select--")] + DomainInformation.StateTerritoryChoices.choices,
widget=ComboboxWidget( error_messages={
attrs={ "required": ("Select the state, territory, or military post where your organization is located.")
"required": True, },
} widget=ComboboxWidget(),
),
) )
class Meta: class Meta:
@ -493,9 +493,6 @@ class DomainOrgNameAddressForm(forms.ModelForm):
"organization_name": {"required": "Enter the name of your organization."}, "organization_name": {"required": "Enter the name of your organization."},
"address_line1": {"required": "Enter the street address of your organization."}, "address_line1": {"required": "Enter the street address of your organization."},
"city": {"required": "Enter the city where your organization is located."}, "city": {"required": "Enter the city where your organization is located."},
"state_territory": {
"required": "Select the state, territory, or military post where your organization is located."
},
} }
widgets = { widgets = {
"organization_name": forms.TextInput, "organization_name": forms.TextInput,

View file

@ -37,7 +37,10 @@ class PortfolioOrgAddressForm(forms.ModelForm):
state_territory = forms.ChoiceField( state_territory = forms.ChoiceField(
label="State, territory, or military post", label="State, territory, or military post",
required=True, required=True,
choices=DomainInformation.StateTerritoryChoices.choices, choices=[("", "--Select--")] + DomainInformation.StateTerritoryChoices.choices,
error_messages={
"required": ("Select the state, territory, or military post where your organization is located.")
},
widget=ComboboxWidget, widget=ComboboxWidget,
) )
@ -54,9 +57,6 @@ class PortfolioOrgAddressForm(forms.ModelForm):
error_messages = { error_messages = {
"address_line1": {"required": "Enter the street address of your organization."}, "address_line1": {"required": "Enter the street address of your organization."},
"city": {"required": "Enter the city where your organization is located."}, "city": {"required": "Enter the city where your organization is located."},
"state_territory": {
"required": "Select the state, territory, or military post where your organization is located."
},
"zipcode": {"required": "Enter a 5-digit or 9-digit zip code, like 12345 or 12345-6789."}, "zipcode": {"required": "Enter a 5-digit or 9-digit zip code, like 12345 or 12345-6789."},
} }
widgets = { widgets = {