From c821b20e8eaed6b53d0e902483d233560e581e76 Mon Sep 17 00:00:00 2001 From: matthewswspence Date: Fri, 28 Feb 2025 12:54:51 -0600 Subject: [PATCH] push remote for demo --- .../src/js/getgov/domain-purpose-form.js | 44 +++++++++++++++ src/registrar/assets/src/js/getgov/main.js | 10 +++- src/registrar/assets/src/js/getgov/radios.js | 56 ++++++++++++++++++- .../forms/domainrequestwizard/purpose.py | 7 --- .../templates/domain_request_purpose.html | 40 ++++++------- src/registrar/views/domain_request.py | 21 +++++-- 6 files changed, 138 insertions(+), 40 deletions(-) create mode 100644 src/registrar/assets/src/js/getgov/domain-purpose-form.js diff --git a/src/registrar/assets/src/js/getgov/domain-purpose-form.js b/src/registrar/assets/src/js/getgov/domain-purpose-form.js new file mode 100644 index 000000000..fa13305b6 --- /dev/null +++ b/src/registrar/assets/src/js/getgov/domain-purpose-form.js @@ -0,0 +1,44 @@ +import { showElement } from './helpers.js'; + +export const domain_purpose_choice_callbacks = { + 'new': { + callback: function(value, element) { + console.log("Callback for new") + //show the purpose details container + showElement(element); + // change just the text inside the em tag + const labelElement = element.querySelector('.usa-label em'); + labelElement.innerHTML = 'Explain why a new domain is required and why a ' + + 'subdomain of an existing domain doesn\'t meet your needs.' + + '

' + // Adding double line break for spacing + 'Include any data that supports a clear public benefit or ' + + 'evidence user need for this new domain. ' + + '*'; + }, + element: document.getElementById('domain-purpose-details-container') + }, + 'redirect': { + callback: function(value, element) { + console.log("Callback for redirect") + // show the purpose details container + showElement(element); + // change just the text inside the em tag + const labelElement = element.querySelector('.usa-label em'); + labelElement.innerHTML = 'Explain why a redirect is necessary. ' + + '*'; + }, + element: document.getElementById('domain-purpose-details-container') + }, + 'other': { + callback: function(value, element) { + console.log("Callback for other") + // Show the purpose details container + showElement(element); + // change just the text inside the em tag + const labelElement = element.querySelector('.usa-label em'); + labelElement.innerHTML = 'Describe how this domain will be used. ' + + '*'; + }, + element: document.getElementById('domain-purpose-details-container') + } +} \ No newline at end of file diff --git a/src/registrar/assets/src/js/getgov/main.js b/src/registrar/assets/src/js/getgov/main.js index 796e6f815..184fd05cb 100644 --- a/src/registrar/assets/src/js/getgov/main.js +++ b/src/registrar/assets/src/js/getgov/main.js @@ -1,4 +1,4 @@ -import { hookupYesNoListener, hookupRadioTogglerListener } from './radios.js'; +import { hookupYesNoListener, hookupCallbacksToRadioToggler } from './radios.js'; import { initDomainValidators } from './domain-validators.js'; import { initFormsetsForms, triggerModalOnDsDataForm, nameserversFormListener } from './formset-forms.js'; import { initializeUrbanizationToggle } from './urbanization.js'; @@ -15,7 +15,7 @@ import { initDomainManagersPage } from './domain-managers.js'; import { initDomainDSData } from './domain-dsdata.js'; import { initDomainDNSSEC } from './domain-dnssec.js'; import { initFormErrorHandling } from './form-errors.js'; - +import { domain_purpose_choice_callbacks } from './domain-purpose-form.js'; initDomainValidators(); initFormsetsForms(); @@ -27,6 +27,12 @@ hookupYesNoListener("additional_details-has_anything_else_text",'anything-else', hookupYesNoListener("additional_details-has_cisa_representative",'cisa-representative', null); hookupYesNoListener("dotgov_domain-feb_naming_requirements", null, "domain-naming-requirements-details-container"); +hookupCallbacksToRadioToggler("purpose-feb_purpose_choice", domain_purpose_choice_callbacks); + +hookupYesNoListener("purpose-has_timeframe", "domain-timeframe-details-container", null); +hookupYesNoListener("purpose-is_interagency_initiative", "domain-interagency-initaitive-details-container", null); + + initializeUrbanizationToggle(); userProfileListener(); diff --git a/src/registrar/assets/src/js/getgov/radios.js b/src/registrar/assets/src/js/getgov/radios.js index 055bdf621..d5feb05d5 100644 --- a/src/registrar/assets/src/js/getgov/radios.js +++ b/src/registrar/assets/src/js/getgov/radios.js @@ -17,7 +17,7 @@ export function hookupYesNoListener(radioButtonName, elementIdToShowIfYes, eleme 'False': elementIdToShowIfNo }); } - + /** * Hookup listeners for radio togglers in form fields. * @@ -75,3 +75,57 @@ export function hookupRadioTogglerListener(radioButtonName, valueToElementMap) { handleRadioButtonChange(); } } + +/** + * Hookup listeners for radio togglers in form fields. + * + * Parameters: + * - radioButtonName: The "name=" value for the radio buttons being used as togglers + * - valueToCallbackMap: An object where keys are the values of the radio buttons, + * and values are dictionaries containing a 'callback' key and an optional 'element' key. + * If provided, the element will be passed in as the second argument to the callback function. + * + * Usage Example: + * Assuming you have radio buttons with values 'option1', 'option2', and 'option3', + * and corresponding callback functions 'function1', 'function2', 'function3' that will + * apply to elements 'element1', 'element2', 'element3' respectively. + * + * hookupCallbacksToRadioToggler('exampleRadioGroup', { + * 'option1': {callback: function1, element: element1}, + * 'option2': {callback: function2, element: element2}, + * 'option3': {callback: function3} // No element provided + * }); + * + * Picking the 'option1' radio button will call function1('option1', element1). + * Picking the 'option3' radio button will call function3('option3') without a second parameter. + **/ +export function hookupCallbacksToRadioToggler(radioButtonName, valueToCallbackMap) { + // Get the radio buttons + let radioButtons = document.querySelectorAll(`input[name="${radioButtonName}"]`); + + function handleRadioButtonChange() { + // Find the checked radio button + let radioButtonChecked = document.querySelector(`input[name="${radioButtonName}"]:checked`); + let selectedValue = radioButtonChecked ? radioButtonChecked.value : null; + + // Execute the callback function for the selected value + if (selectedValue && valueToCallbackMap[selectedValue]) { + const entry = valueToCallbackMap[selectedValue]; + if ('element' in entry) { + entry.callback(selectedValue, entry.element); + } else { + entry.callback(selectedValue); + } + } + } + + if (radioButtons && radioButtons.length) { + // Add event listener to each radio button + radioButtons.forEach(function (radioButton) { + radioButton.addEventListener('change', handleRadioButtonChange); + }); + + // Initialize by checking the current state + handleRadioButtonChange(); + } +} \ No newline at end of file diff --git a/src/registrar/forms/domainrequestwizard/purpose.py b/src/registrar/forms/domainrequestwizard/purpose.py index 23e5c28e9..52946a5e6 100644 --- a/src/registrar/forms/domainrequestwizard/purpose.py +++ b/src/registrar/forms/domainrequestwizard/purpose.py @@ -20,13 +20,6 @@ class FEBPurposeOptionsForm(BaseDeletableRegistrarForm): class PurposeDetailsForm(BaseDeletableRegistrarForm): - labels = { - "new": "Explain why a new domain is required and why a subdomain of an existing domain doesn't meet your needs. \ - Include any data that supports a clear public benefit or evident user need for this new domain.", - "redirect": "Explain why a redirect is necessary", - "other": "Describe how this domain will be used", - } - field_name="purpose" purpose = forms.CharField( diff --git a/src/registrar/templates/domain_request_purpose.html b/src/registrar/templates/domain_request_purpose.html index bef221c4b..a7d5ddbfd 100644 --- a/src/registrar/templates/domain_request_purpose.html +++ b/src/registrar/templates/domain_request_purpose.html @@ -7,10 +7,6 @@

What is the purpose of your requested domain?

{% endblock %} -{% block form_required_fields_help_text %} -{# commented out so it does not appear on this page #} -{% endblock %} - {% block form_fields %} {% if requires_feb_questions %}
@@ -21,60 +17,56 @@ {{forms.4.management_form}} {{forms.5.management_form}}

- Select One * + Select One *

{% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %} {% input_with_errors forms.0.feb_purpose_choice %} {% endwith %} -
{% else %} diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py index 2d755c636..9ecd66191 100644 --- a/src/registrar/views/domain_request.py +++ b/src/registrar/views/domain_request.py @@ -745,26 +745,35 @@ class Purpose(DomainRequestWizard): feb_initiative_details_form = forms_list[5] if not self.requires_feb_questions(): - # if FEB questions don't apply, mark all other forms for deletion + # if FEB questions don't apply, mark those forms for deletion feb_purpose_options_form.mark_form_for_deletion() feb_timeframe_yes_no_form.mark_form_for_deletion() feb_timeframe_details_form.mark_form_for_deletion() feb_initiative_yes_no_form.mark_form_for_deletion() feb_initiative_details_form.mark_form_for_deletion() - # we only care about the purpose form in this case since it's used in both instances + # we only care about the purpose details form in this case since it's used in both instances return purpose_details_form.is_valid() if not feb_purpose_options_form.is_valid(): # Ensure details form doesn't throw errors if it's not showing purpose_details_form.mark_form_for_deletion() + + feb_timeframe_valid = feb_timeframe_yes_no_form.is_valid() + feb_initiative_valid = feb_initiative_yes_no_form.is_valid() + + logger.debug(f"feb timeframe yesno: {feb_timeframe_yes_no_form.cleaned_data.get('has_timeframe')}") + logger.debug(f"FEB initiative yesno: {feb_initiative_yes_no_form.cleaned_data.get('is_interagency_initiative')}") - if not feb_initiative_yes_no_form.is_valid() or not feb_timeframe_yes_no_form.cleaned_data.get("has_timeframe"): + if not feb_timeframe_valid or not feb_timeframe_yes_no_form.cleaned_data.get("has_timeframe"): + # Ensure details form doesn't throw errors if it's not showing + feb_timeframe_details_form.mark_form_for_deletion() + + if not feb_initiative_valid or not feb_initiative_yes_no_form.cleaned_data.get("is_interagency_initiative"): # Ensure details form doesn't throw errors if it's not showing feb_initiative_details_form.mark_form_for_deletion() - if not feb_timeframe_yes_no_form.is_valid() or not feb_initiative_yes_no_form.cleaned_data.get("is_interagency_initiative"): - # Ensure details form doesn't throw errors if it's not showing - feb_timeframe_details_form.mark_form_for_deletion() + for i, form in enumerate(forms_list): + logger.debug(f"Form {i} is marked for deletion: {form.form_data_marked_for_deletion}") valid = all(form.is_valid() for form in forms_list if not form.form_data_marked_for_deletion)