Repository: cisagov/manage.get.gov

Issue:
Update "Submit your domain request" button behavior  
### Issue description

As a Registrant Applicant, I want to understand the importance and implications upon clicking the final submit button so that I'm made aware that this is the final stage and I will no longer be allowed to edit my application.

Implement solution for "Submit your domain request" button base on current design.
Figma showing showing user experience: [Prototype](https://www.figma.com/proto/aJbuDLJNNikqQObTuCNKQa/GetGov_Designs_ECS-Truss?page-id=2314%3A49899&type=design&node-id=2314-50557&viewport=-2156%2C155%2C0.32&t=7Mt0wTODqUSsxU1o-1&scaling=min-zoom&starting-point-node-id=2314%3A50557&mode=design) and [Screens](https://www.figma.com/file/aJbuDLJNNikqQObTuCNKQa/GetGov_Designs_ECS-Truss?type=design&node-id=2314-49899&mode=design)


### Acceptance criteria

- [ ] Clicking "Submit your domain request" button triggers a modal
- [ ] The modal headline says: `You are about to submit a domain request for <requesteddomain.gov>. `
- [ ] The body says: `Once you submit this request, you won’t be able to make further edits until it’s reviewed by our staff. You’ll only be able to withdraw your request. `
- [ ] The button says: `Submit request`
- [ ] The link button says: `Go back`
- [ ] User cannot click out of modal, they can exit back to the Review page by clicking "Go back" or the "X" icon. 
- [ ] Clicking the primary button submits their request and shows them the Confirmation page

### Additional context

_No response_

### Links to other issues

🔄 Corresponding design ticket #767


Discussion:

Resolving patch:
diff --git a/src/registrar/views/application.py b/src/registrar/views/application.py
--- a/src/registrar/views/application.py
+++ b/src/registrar/views/application.py
@@ -321,12 +321,21 @@ def pending_applications(self):
 
     def get_context_data(self):
         """Define context for access on all wizard pages."""
+        # Build the submit button that we'll pass to the modal.
+        modal_button = '<button type="submit" ' 'class="usa-button" ' ">Submit request</button>"
+        # Concatenate the modal header that we'll pass to the modal.
+        if self.application.requested_domain:
+            modal_heading = "You are about to submit a domain request for " + str(self.application.requested_domain)
+        else:
+            modal_heading = "You are about to submit an incomplete request"
         return {
             "form_titles": self.TITLES,
             "steps": self.steps,
             # Add information about which steps should be unlocked
             "visited": self.storage.get("step_history", []),
             "is_federal": self.application.is_federal(),
+            "modal_button": modal_button,
+            "modal_heading": modal_heading,
         }
 
     def get_step_list(self) -> list:


Test patch:
diff --git a/src/registrar/tests/test_copy_names_from_contacts_to_users.py b/src/registrar/tests/test_copy_names_from_contacts_to_users.py
--- a/src/registrar/tests/test_copy_names_from_contacts_to_users.py
+++ b/src/registrar/tests/test_copy_names_from_contacts_to_users.py
@@ -18,8 +18,8 @@ def setUp(self):
         self.user2 = User.objects.create(username="user2")
         self.user3 = User.objects.create(username="user3")
         self.user4 = User.objects.create(username="user4")
-        # The last user created triggers the creation of a contact and attaches itself to it. @Neil wth is going on?
-        # This bs_user defuses that situation so we can test the code.
+        # The last user created triggers the creation of a contact and attaches itself to it. See signals.
+        # This bs_user defuses that situation.
         self.bs_user = User.objects.create()
 
         self.contact1 = Contact.objects.create(
diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py
--- a/src/registrar/tests/test_views.py
+++ b/src/registrar/tests/test_views.py
@@ -141,7 +141,7 @@ def test_application_form_empty_submit(self):
         # 302 redirect to the first form
         page = self.app.get(reverse("application:")).follow()
         # submitting should get back the same page if the required field is empty
-        result = page.form.submit()
+        result = page.forms[0].submit()
         self.assertIn("What kind of U.S.-based government organization do you represent?", result)
 
     def test_application_multiple_applications_exist(self):
@@ -164,6 +164,9 @@ def test_application_form_submission(self):
         this test work.
 
         This test also looks for the long organization name on the summary page.
+
+        This also tests for the presence of a modal trigger and the dynamic test
+        in the modal header on the submit page.
         """
         num_pages_tested = 0
         # elections, type_of_work, tribal_government, no_other_contacts
@@ -178,11 +181,11 @@ def test_application_form_submission(self):
         session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
 
         # ---- TYPE PAGE  ----
-        type_form = type_page.form
+        type_form = type_page.forms[0]
         type_form["organization_type-organization_type"] = "federal"
         # test next button and validate data
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
-        type_result = type_page.form.submit()
+        type_result = type_form.submit()
         # should see results in db
         application = DomainApplication.objects.get()  # there's only one
         self.assertEqual(application.organization_type, "federal")
@@ -197,7 +200,7 @@ def test_application_form_submission(self):
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
 
         federal_page = type_result.follow()
-        federal_form = federal_page.form
+        federal_form = federal_page.forms[0]
         federal_form["organization_federal-federal_type"] = "executive"
 
         # test next button
@@ -216,7 +219,7 @@ def test_application_form_submission(self):
         # Follow the redirect to the next form page
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         org_contact_page = federal_result.follow()
-        org_contact_form = org_contact_page.form
+        org_contact_form = org_contact_page.forms[0]
         # federal agency so we have to fill in federal_agency
         org_contact_form["organization_contact-federal_agency"] = "General Services Administration"
         org_contact_form["organization_contact-organization_name"] = "Testorg"
@@ -249,7 +252,7 @@ def test_application_form_submission(self):
         # Follow the redirect to the next form page
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         ao_page = org_contact_result.follow()
-        ao_form = ao_page.form
+        ao_form = ao_page.forms[0]
         ao_form["authorizing_official-first_name"] = "Testy ATO"
         ao_form["authorizing_official-last_name"] = "Tester ATO"
         ao_form["authorizing_official-title"] = "Chief Tester"
@@ -276,7 +279,7 @@ def test_application_form_submission(self):
         # Follow the redirect to the next form page
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         current_sites_page = ao_result.follow()
-        current_sites_form = current_sites_page.form
+        current_sites_form = current_sites_page.forms[0]
         current_sites_form["current_sites-0-website"] = "www.city.com"
 
         # test next button
@@ -298,7 +301,7 @@ def test_application_form_submission(self):
         # Follow the redirect to the next form page
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         dotgov_page = current_sites_result.follow()
-        dotgov_form = dotgov_page.form
+        dotgov_form = dotgov_page.forms[0]
         dotgov_form["dotgov_domain-requested_domain"] = "city"
         dotgov_form["dotgov_domain-0-alternative_domain"] = "city1"
 
@@ -318,7 +321,7 @@ def test_application_form_submission(self):
         # Follow the redirect to the next form page
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         purpose_page = dotgov_result.follow()
-        purpose_form = purpose_page.form
+        purpose_form = purpose_page.forms[0]
         purpose_form["purpose-purpose"] = "For all kinds of things."
 
         # test next button
@@ -337,7 +340,7 @@ def test_application_form_submission(self):
         # Follow the redirect to the next form page
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         your_contact_page = purpose_result.follow()
-        your_contact_form = your_contact_page.form
+        your_contact_form = your_contact_page.forms[0]
 
         your_contact_form["your_contact-first_name"] = "Testy you"
         your_contact_form["your_contact-last_name"] = "Tester you"
@@ -365,7 +368,7 @@ def test_application_form_submission(self):
         # Follow the redirect to the next form page
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         other_contacts_page = your_contact_result.follow()
-        other_contacts_form = other_contacts_page.form
+        other_contacts_form = other_contacts_page.forms[0]
 
         other_contacts_form["other_contacts-0-first_name"] = "Testy2"
         other_contacts_form["other_contacts-0-last_name"] = "Tester2"
@@ -398,7 +401,7 @@ def test_application_form_submission(self):
         # Follow the redirect to the next form page
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         anything_else_page = other_contacts_result.follow()
-        anything_else_form = anything_else_page.form
+        anything_else_form = anything_else_page.forms[0]
 
         anything_else_form["anything_else-anything_else"] = "Nothing else."
 
@@ -418,7 +421,7 @@ def test_application_form_submission(self):
         # Follow the redirect to the next form page
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         requirements_page = anything_else_result.follow()
-        requirements_form = requirements_page.form
+        requirements_form = requirements_page.forms[0]
 
         requirements_form["requirements-is_policy_acknowledged"] = True
 
@@ -438,7 +441,7 @@ def test_application_form_submission(self):
         # Follow the redirect to the next form page
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         review_page = requirements_result.follow()
-        review_form = review_page.form
+        review_form = review_page.forms[0]
 
         # Review page contains all the previously entered data
         # Let's make sure the long org name is displayed
@@ -472,6 +475,14 @@ def test_application_form_submission(self):
         self.assertContains(review_page, "(201) 555-5557")
         self.assertContains(review_page, "Nothing else.")
 
+        # We can't test the modal itself as it relies on JS for init and triggering,
+        # but we can test for the existence of its trigger:
+        self.assertContains(review_page, "toggle-submit-domain-request")
+        # And the existence of the modal's data parked and ready for the js init.
+        # The next assert also tests for the passed requested domain context from
+        # the view > application_form > modal
+        self.assertContains(review_page, "You are about to submit a domain request for city.gov")
+
         # final submission results in a redirect to the "finished" URL
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         with less_console_noise():
@@ -540,7 +551,7 @@ def test_application_form_conditional_federal(self):
         # the conditional step titles shouldn't appear initially
         self.assertNotContains(type_page, self.TITLES["organization_federal"])
         self.assertNotContains(type_page, self.TITLES["organization_election"])
-        type_form = type_page.form
+        type_form = type_page.forms[0]
         type_form["organization_type-organization_type"] = "federal"
 
         # set the session ID before .submit()
@@ -561,9 +572,9 @@ def test_application_form_conditional_federal(self):
 
         # continuing on in the flow we need to see top-level agency on the
         # contact page
-        federal_page.form["organization_federal-federal_type"] = "executive"
+        federal_page.forms[0]["organization_federal-federal_type"] = "executive"
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
-        federal_result = federal_page.form.submit()
+        federal_result = federal_page.forms[0].submit()
         # the post request should return a redirect to the contact
         # question
         self.assertEqual(federal_result.status_code, 302)
@@ -586,7 +597,7 @@ def test_application_form_conditional_elections(self):
         # the conditional step titles shouldn't appear initially
         self.assertNotContains(type_page, self.TITLES["organization_federal"])
         self.assertNotContains(type_page, self.TITLES["organization_election"])
-        type_form = type_page.form
+        type_form = type_page.forms[0]
         type_form["organization_type-organization_type"] = "county"
 
         # set the session ID before .submit()
@@ -606,9 +617,9 @@ def test_application_form_conditional_elections(self):
 
         # continuing on in the flow we need to NOT see top-level agency on the
         # contact page
-        election_page.form["organization_election-is_election_board"] = "True"
+        election_page.forms[0]["organization_election-is_election_board"] = "True"
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
-        election_result = election_page.form.submit()
+        election_result = election_page.forms[0].submit()
         # the post request should return a redirect to the contact
         # question
         self.assertEqual(election_result.status_code, 302)
@@ -626,10 +637,10 @@ def test_application_form_section_skipping(self):
         # and then setting the cookie on each request.
         session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
 
-        type_form = type_page.form
+        type_form = type_page.forms[0]
         type_form["organization_type-organization_type"] = "federal"
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
-        type_result = type_page.form.submit()
+        type_result = type_form.submit()
 
         # follow first redirect
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@@ -654,15 +665,15 @@ def test_application_form_nonfederal(self):
         # and then setting the cookie on each request.
         session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
 
-        type_form = type_page.form
+        type_form = type_page.forms[0]
         type_form["organization_type-organization_type"] = DomainApplication.OrganizationChoices.INTERSTATE
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
-        type_result = type_page.form.submit()
+        type_result = type_form.submit()
 
         # follow first redirect
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         contact_page = type_result.follow()
-        org_contact_form = contact_page.form
+        org_contact_form = contact_page.forms[0]
 
         self.assertNotIn("federal_agency", org_contact_form.fields)
 
@@ -690,10 +701,10 @@ def test_application_about_your_organization_special(self):
         # and then setting the cookie on each request.
         session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
 
-        type_form = type_page.form
+        type_form = type_page.forms[0]
         type_form["organization_type-organization_type"] = DomainApplication.OrganizationChoices.SPECIAL_DISTRICT
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
-        type_result = type_page.form.submit()
+        type_result = type_page.forms[0].submit()
         # follow first redirect
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         contact_page = type_result.follow()
@@ -710,7 +721,7 @@ def test_application_no_other_contacts(self):
         session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
 
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
-        result = contacts_page.form.submit()
+        result = contacts_page.forms[0].submit()
         # follow first redirect
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         no_contacts_page = result.follow()
@@ -727,10 +738,10 @@ def test_application_about_your_organiztion_interstate(self):
         # and then setting the cookie on each request.
         session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
 
-        type_form = type_page.form
+        type_form = type_page.forms[0]
         type_form["organization_type-organization_type"] = DomainApplication.OrganizationChoices.INTERSTATE
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
-        type_result = type_page.form.submit()
+        type_result = type_form.submit()
         # follow first redirect
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         contact_page = type_result.follow()
@@ -745,10 +756,10 @@ def test_application_tribal_government(self):
         # of a "session". We are going to do it manually, saving the session ID here
         # and then setting the cookie on each request.
         session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
-        type_form = type_page.form
+        type_form = type_page.forms[0]
         type_form["organization_type-organization_type"] = DomainApplication.OrganizationChoices.TRIBAL
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
-        type_result = type_page.form.submit()
+        type_result = type_form.submit()
         # the tribal government page comes immediately afterwards
         self.assertIn("/tribal_government", type_result.headers["Location"])
         # follow first redirect
@@ -767,18 +778,18 @@ def test_application_ao_dynamic_text(self):
         session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
 
         # ---- TYPE PAGE  ----
-        type_form = type_page.form
+        type_form = type_page.forms[0]
         type_form["organization_type-organization_type"] = "federal"
 
         # test next button
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
-        type_result = type_page.form.submit()
+        type_result = type_form.submit()
 
         # ---- FEDERAL BRANCH PAGE  ----
         # Follow the redirect to the next form page
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         federal_page = type_result.follow()
-        federal_form = federal_page.form
+        federal_form = federal_page.forms[0]
         federal_form["organization_federal-federal_type"] = "executive"
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         federal_result = federal_form.submit()
@@ -787,7 +798,7 @@ def test_application_ao_dynamic_text(self):
         # Follow the redirect to the next form page
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         org_contact_page = federal_result.follow()
-        org_contact_form = org_contact_page.form
+        org_contact_form = org_contact_page.forms[0]
         # federal agency so we have to fill in federal_agency
         org_contact_form["organization_contact-federal_agency"] = "General Services Administration"
         org_contact_form["organization_contact-organization_name"] = "Testorg"
@@ -828,18 +839,18 @@ def test_application_dotgov_domain_dynamic_text(self):
         # and then setting the cookie on each request.
         session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
         # ---- TYPE PAGE  ----
-        type_form = type_page.form
+        type_form = type_page.forms[0]
         type_form["organization_type-organization_type"] = "federal"
 
         # test next button
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
-        type_result = type_page.form.submit()
+        type_result = type_form.submit()
 
         # ---- FEDERAL BRANCH PAGE  ----
         # Follow the redirect to the next form page
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         federal_page = type_result.follow()
-        federal_form = federal_page.form
+        federal_form = federal_page.forms[0]
         federal_form["organization_federal-federal_type"] = "executive"
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         federal_result = federal_form.submit()
@@ -848,7 +859,7 @@ def test_application_dotgov_domain_dynamic_text(self):
         # Follow the redirect to the next form page
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         org_contact_page = federal_result.follow()
-        org_contact_form = org_contact_page.form
+        org_contact_form = org_contact_page.forms[0]
         # federal agency so we have to fill in federal_agency
         org_contact_form["organization_contact-federal_agency"] = "General Services Administration"
         org_contact_form["organization_contact-organization_name"] = "Testorg"
@@ -870,7 +881,7 @@ def test_application_dotgov_domain_dynamic_text(self):
         # Follow the redirect to the next form page
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         ao_page = org_contact_result.follow()
-        ao_form = ao_page.form
+        ao_form = ao_page.forms[0]
         ao_form["authorizing_official-first_name"] = "Testy ATO"
         ao_form["authorizing_official-last_name"] = "Tester ATO"
         ao_form["authorizing_official-title"] = "Chief Tester"
@@ -884,7 +895,7 @@ def test_application_dotgov_domain_dynamic_text(self):
         # Follow the redirect to the next form page
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         current_sites_page = ao_result.follow()
-        current_sites_form = current_sites_page.form
+        current_sites_form = current_sites_page.forms[0]
         current_sites_form["current_sites-0-website"] = "www.city.com"
 
         # test saving the page
@@ -917,7 +928,7 @@ def test_application_formsets(self):
         current_sites_page = self.app.get(reverse("application:current_sites"))
         session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
         # fill in the form field
-        current_sites_form = current_sites_page.form
+        current_sites_form = current_sites_page.forms[0]
         self.assertIn("current_sites-0-website", current_sites_form.fields)
         self.assertNotIn("current_sites-1-website", current_sites_form.fields)
         current_sites_form["current_sites-0-website"] = "https://example.com"
@@ -926,7 +937,7 @@ def test_application_formsets(self):
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
         current_sites_result = current_sites_form.submit("submit_button", value="save")
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
-        current_sites_form = current_sites_result.follow().form
+        current_sites_form = current_sites_result.follow().forms[0]
 
         # verify that there are two form fields
         value = current_sites_form["current_sites-0-website"].value
@@ -1086,6 +1097,18 @@ def test_long_org_name_in_application_manage(self):
         detail_page = home_page.click("Manage", index=0)
         self.assertContains(detail_page, "Federal: an agency of the U.S. government")
 
+    def test_submit_modal_no_domain_text_fallback(self):
+        """When user clicks on submit your domain request and the requested domain
+        is null (possible through url direct access to the review page), present
+        fallback copy in the modal's header.
+
+        NOTE: This may be a moot point if we implement a more solid pattern in the
+        future, like not a submit action at all on the review page."""
+
+        review_page = self.app.get(reverse("application:review"))
+        self.assertContains(review_page, "toggle-submit-domain-request")
+        self.assertContains(review_page, "You are about to submit an incomplete request")
+
 
 class TestWithDomainPermissions(TestWithUser):
     def setUp(self):