From f7645228dd69272d0e3b419194d50f8edb60878b Mon Sep 17 00:00:00 2001 From: Jaxon Silva Date: Thu, 13 Feb 2025 10:46:47 -0800 Subject: [PATCH 01/14] Removed thank you text from ineligible states --- src/registrar/templates/emails/status_change_rejected.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/registrar/templates/emails/status_change_rejected.txt b/src/registrar/templates/emails/status_change_rejected.txt index e56d46a1f..2fc44c504 100644 --- a/src/registrar/templates/emails/status_change_rejected.txt +++ b/src/registrar/templates/emails/status_change_rejected.txt @@ -69,8 +69,10 @@ NEED ASSISTANCE? If you have questions about this domain request or need help choosing a new domain name, reply to this email. {% endif %} +{% if reason != domain_request.RejectionReasons.REQUESTOR_NOT_ELIGIBLE %} THANK YOU .Gov helps the public identify official, trusted information. Thank you for requesting a .gov domain. +{% endif %} ---------------------------------------------------------------- From 2c75a8dfbd509032c05ca47b40c846fec0db308e Mon Sep 17 00:00:00 2001 From: Jaxon Silva Date: Thu, 13 Feb 2025 11:06:01 -0800 Subject: [PATCH 02/14] Update status_change_rejected.txt --- src/registrar/templates/emails/status_change_rejected.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/templates/emails/status_change_rejected.txt b/src/registrar/templates/emails/status_change_rejected.txt index 2fc44c504..a222e6088 100644 --- a/src/registrar/templates/emails/status_change_rejected.txt +++ b/src/registrar/templates/emails/status_change_rejected.txt @@ -68,12 +68,12 @@ Learn more about: NEED ASSISTANCE? If you have questions about this domain request or need help choosing a new domain name, reply to this email. {% endif %} - {% if reason != domain_request.RejectionReasons.REQUESTOR_NOT_ELIGIBLE %} + THANK YOU .Gov helps the public identify official, trusted information. Thank you for requesting a .gov domain. -{% endif %} +{% endif %} ---------------------------------------------------------------- The .gov team From 3a12e59db65c2a76a4151603e6d2442dd8cb0f24 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 18 Feb 2025 15:02:53 -0700 Subject: [PATCH 03/14] Fix andi errors --- src/registrar/admin.py | 7 ++++--- src/registrar/assets/src/sass/_theme/_admin.scss | 4 ++++ .../templates/admin/change_list_results.html | 10 +++++----- src/registrar/templatetags/custom_filters.py | 14 +++++++++----- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 2d2b90a5f..9dae19a22 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -2287,11 +2287,12 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportModelAdmin): @admin.display(description=_("Requested Domain")) def custom_requested_domain(self, obj): # Example: Show different icons based on `status` - url = reverse("admin:registrar_domainrequest_changelist") + f"{obj.id}" text = obj.requested_domain if obj.portfolio: - return format_html(' {}', url, text) - return format_html('{}', url, text) + return format_html( + f' {escape(text)}' + ) + return text custom_requested_domain.admin_order_field = "requested_domain__name" # type: ignore diff --git a/src/registrar/assets/src/sass/_theme/_admin.scss b/src/registrar/assets/src/sass/_theme/_admin.scss index a15d1eabe..79fc6273e 100644 --- a/src/registrar/assets/src/sass/_theme/_admin.scss +++ b/src/registrar/assets/src/sass/_theme/_admin.scss @@ -982,3 +982,7 @@ ul.add-list-reset { } } + +#result_list > tbody tr > th > a { + text-decoration: underline; +} \ No newline at end of file diff --git a/src/registrar/templates/admin/change_list_results.html b/src/registrar/templates/admin/change_list_results.html index 5e4f37711..f55ac7197 100644 --- a/src/registrar/templates/admin/change_list_results.html +++ b/src/registrar/templates/admin/change_list_results.html @@ -19,11 +19,11 @@ Load our custom filters to extract info from the django generated markup. {% if results.0|contains_checkbox %} {# .gov - hardcode the select all checkbox #} - +
- +
@@ -61,10 +61,10 @@ Load our custom filters to extract info from the django generated markup. {% endif %} {% with result_value=result.0|extract_value %} - {% with result_label=result.1|extract_a_text %} + {% with result_label=result.1|extract_a_text checkbox_id="select-"|add:result_value %} - - + + {% endwith %} {% endwith %} diff --git a/src/registrar/templatetags/custom_filters.py b/src/registrar/templatetags/custom_filters.py index ff73e6dc1..cf2abf447 100644 --- a/src/registrar/templatetags/custom_filters.py +++ b/src/registrar/templatetags/custom_filters.py @@ -25,11 +25,15 @@ def extract_a_text(value): pattern = r"]*>(.*?)" match = re.search(pattern, value) if match: - extracted_text = match.group(1) - else: - extracted_text = "" - - return extracted_text + # Get the content and strip any nested HTML tags + content = match.group(1) + # Remove any nested HTML tags (like ) + text_pattern = r"<[^>]+>" + text_only = re.sub(text_pattern, "", content) + # Clean up any extra whitespace + return text_only.strip() + + return "" @register.filter From 0e09e5720bc49b9ecf677be53f7bbceaf412c52f Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 19 Feb 2025 10:45:47 -0700 Subject: [PATCH 04/14] Test making button clickable --- .../assets/src/js/getgov-admin/button-utils.js | 16 ++++++++++++++++ src/registrar/assets/src/js/getgov-admin/main.js | 2 ++ .../import_export/change_list_export_item.html | 7 +++++++ .../import_export/change_list_import_item.html | 2 +- 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/registrar/assets/src/js/getgov-admin/button-utils.js create mode 100644 src/registrar/templates/admin/import_export/change_list_export_item.html diff --git a/src/registrar/assets/src/js/getgov-admin/button-utils.js b/src/registrar/assets/src/js/getgov-admin/button-utils.js new file mode 100644 index 000000000..79d9091d6 --- /dev/null +++ b/src/registrar/assets/src/js/getgov-admin/button-utils.js @@ -0,0 +1,16 @@ +/** + * Initializes buttons to behave like links by navigating to their data-url attribute + * Example usage: + */ +export function initButtonLinks() { + document.querySelectorAll('button.use-button-as-link').forEach(button => { + button.addEventListener('click', function() { + // Equivalent to button.getAttribute("data-href") + const href = this.dataset.href; + console.log(`in loop: ${href}`) + if (href) { + window.location.href = href; + } + }); + }); +} diff --git a/src/registrar/assets/src/js/getgov-admin/main.js b/src/registrar/assets/src/js/getgov-admin/main.js index 5c6de20ab..7eb1fc8cd 100644 --- a/src/registrar/assets/src/js/getgov-admin/main.js +++ b/src/registrar/assets/src/js/getgov-admin/main.js @@ -16,6 +16,7 @@ import { initDynamicPortfolioFields } from './portfolio-form.js'; import { initDynamicDomainInformationFields } from './domain-information-form.js'; import { initDynamicDomainFields } from './domain-form.js'; import { initAnalyticsDashboard } from './analytics.js'; +import { initButtonLinks } from './button-utils.js'; // General initModals(); @@ -23,6 +24,7 @@ initCopyToClipboard(); initFilterHorizontalWidget(); initDescriptions(); initSubmitBar(); +initButtonLinks(); // Domain request initIneligibleModal(); diff --git a/src/registrar/templates/admin/import_export/change_list_export_item.html b/src/registrar/templates/admin/import_export/change_list_export_item.html new file mode 100644 index 000000000..9678d224a --- /dev/null +++ b/src/registrar/templates/admin/import_export/change_list_export_item.html @@ -0,0 +1,7 @@ +{% load i18n %} +{% load admin_urls %} + +{% if has_export_permission %} +{% comment %} Uses the initButtonLinks {% endcomment %} +
  • +{% endif %} diff --git a/src/registrar/templates/admin/import_export/change_list_import_item.html b/src/registrar/templates/admin/import_export/change_list_import_item.html index 8255a8ba7..0f2d59421 100644 --- a/src/registrar/templates/admin/import_export/change_list_import_item.html +++ b/src/registrar/templates/admin/import_export/change_list_import_item.html @@ -3,6 +3,6 @@ {% if has_import_permission %} {% if not IS_PRODUCTION %} -
  • {% trans "Import" %}
  • +
  • {% endif %} {% endif %} From 4a634936e65d6bc8df5208f3b2e2abdadd270c67 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 19 Feb 2025 12:10:16 -0700 Subject: [PATCH 05/14] Change all buttons to
  • - {% translate "View on site" %} +
  • {% else %} @@ -30,18 +30,18 @@ {% endif %}
  • - {% translate "History" %} +
  • {% if opts.model_name == 'domainrequest' %}
  • - +
  • {% endif %} diff --git a/src/registrar/templates/admin/change_list_object_tools.html b/src/registrar/templates/admin/change_list_object_tools.html index 9a046b4bb..5ba88aa3a 100644 --- a/src/registrar/templates/admin/change_list_object_tools.html +++ b/src/registrar/templates/admin/change_list_object_tools.html @@ -5,9 +5,9 @@ {% if has_add_permission %}

    {% url cl.opts|admin_urlname:'add' as add_url %} - +

    {% endif %} {% endblock %} \ No newline at end of file From 20ff3541b43706d609d4349efd444a83f84dbee7 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 19 Feb 2025 13:09:59 -0700 Subject: [PATCH 06/14] fix aria warning --- .../templates/admin/search_form.html | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/registrar/templates/admin/search_form.html diff --git a/src/registrar/templates/admin/search_form.html b/src/registrar/templates/admin/search_form.html new file mode 100644 index 000000000..c5fcf31f8 --- /dev/null +++ b/src/registrar/templates/admin/search_form.html @@ -0,0 +1,26 @@ +{% comment %} This is an override of the django search bar to add better accessibility compliance. +There are no blocks defined here, so we had to copy the code. +https://github.com/django/django/blob/main/django/contrib/admin/templates/admin/search_form.html +{% endcomment %} +{% load i18n static %} +{% if cl.search_fields %} +
    +{% endif %} \ No newline at end of file From 2cfce34edebaefaefa199549de245e9358dbe5d1 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 19 Feb 2025 13:24:15 -0700 Subject: [PATCH 07/14] Add aria-label for table sort buttons --- src/registrar/templates/admin/change_list_results.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/templates/admin/change_list_results.html b/src/registrar/templates/admin/change_list_results.html index f55ac7197..c5be04133 100644 --- a/src/registrar/templates/admin/change_list_results.html +++ b/src/registrar/templates/admin/change_list_results.html @@ -34,9 +34,9 @@ Load our custom filters to extract info from the django generated markup. {% if header.sortable %} {% if header.sort_priority > 0 %}
    - + {% if num_sorted_fields > 1 %}{{ header.sort_priority }}{% endif %} - +
    {% endif %} {% endif %} From 3bfb75fa3bd5a5309f36fd08636ca47dd3d4efa9 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 19 Feb 2025 13:27:35 -0700 Subject: [PATCH 08/14] Update src/registrar/assets/src/sass/_theme/_admin.scss --- src/registrar/assets/src/sass/_theme/_admin.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/assets/src/sass/_theme/_admin.scss b/src/registrar/assets/src/sass/_theme/_admin.scss index f136fb098..bd55bbfcb 100644 --- a/src/registrar/assets/src/sass/_theme/_admin.scss +++ b/src/registrar/assets/src/sass/_theme/_admin.scss @@ -995,4 +995,4 @@ ul.add-list-reset { #result_list > tbody tr > th > a { text-decoration: underline; -} \ No newline at end of file +} From b687219416927824ae1ab44ed2cd5a26d480bdeb Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Wed, 19 Feb 2025 17:22:09 -0500 Subject: [PATCH 09/14] ADR on ajax --- .../0027-ajax-for-dynamic-content.md | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 docs/architecture/decisions/0027-ajax-for-dynamic-content.md diff --git a/docs/architecture/decisions/0027-ajax-for-dynamic-content.md b/docs/architecture/decisions/0027-ajax-for-dynamic-content.md new file mode 100644 index 000000000..6e22a67e7 --- /dev/null +++ b/docs/architecture/decisions/0027-ajax-for-dynamic-content.md @@ -0,0 +1,96 @@ +# 26. Django Waffle library for Feature Flags + +Date: 2024-05-22 (back dated) + +## Status + +Approved + +## Context + +When we decided to implement server-side rendering ([ADR#8 - Server-Side rendering](./0008-server-side-rendering.md)), we identified a potential risk: users and stakeholders might expect increasingly interactive experiences similar to those found in single-page applications (SPAs). Modern JavaScript frameworks such as React, Angular, and Vue enable rich interactivity by allowing applications to update portions of the page dynamically—often without requiring a full-page reload. These frameworks abstract AJAX and DOM manipulation, creating a high-level interface between JavaScript, HTML, and the browser. + +Our decision to use Django for server-rendered pages allowed us to deliver an MVP quickly and facilitated easy onboarding for new developers. However, the anticipated risk materialized, and stakeholders now expect a more seamless, SPA-like experience. + +We already leverage vanilla JavaScript for interactive components throughout the application. These implementations are neatly contained within Immediately Invoked Function Expressions (IIFEs) and are designed to extend specific components without interfering with Django’s server-rendered structure. + +However, new components that require features like pagination, search, and filtering demand a more responsive, real-time user experience. This prompted an exploration of possible solutions. + +## Considered Options + +**Option 1:** Migrate to a Full SPA with Django as a Backend API +This approach involves refactoring Django into a backend-only service and adopting a modern JavaScript framework for the frontend. + +✅ Pros: +- Future-proof solution that aligns with modern web development practices. +- Enables highly interactive and dynamic UI. +- Clean separation of concerns between frontend and backend. + +❌ Cons: +- Requires significant investment in development and infrastructure changes. +- Major refactoring effort, delaying feature delivery. +- Increased complexity for testing and deployment. + +This approach was deemed too costly in terms of both time and resources. + +**Option 2:** Adopt a Modern JS Framework for Select Parts of the Application +Instead of a full migration, this approach involves integrating a modern JavaScript framework (e.g., React or Vue) only in areas that require high interactivity. + +✅ Pros: +- Avoids a complete rewrite, allowing incremental improvements. +- More flexibility in choosing the level of interactivity per feature. + +❌ Cons: +- Introduces multiple frontend paradigms, increasing developer onboarding complexity. +- Requires new deployment and build infrastructure. +- Creates long-term technical debt if legacy Django templates and new JS-driven components coexist indefinitely. + +This approach would still introduce diverging implementation stacks, leading to long-term maintenance challenges. + +**Option 3:**Use a Lightweight JavaScript Framework (e.g., HTMX, HTMZ) +Instead of React or Vue, this approach involves using a minimal JavaScript framework like HTMX or HTMZ to enhance interactivity while preserving Django’s server-rendered structure. + +✅ Pros: +- Reduces the need for a full rewrite. +- Keeps Django templates largely intact. +- Minimizes complexity compared to React or Vue. + +❌ Cons: +- Limited community support and long-term viability concerns. +- Still introduces new technology and learning curves. +- Unclear whether it fully meets our interactivity needs. + +Ultimately, we determined that the benefits did not outweigh the potential downsides. + +**Option 4:** Extend Vanilla JavaScript with AJAX (Selected Option) +This approach involves incrementally enhancing Django’s server-rendered pages with AJAX while maintaining our existing architecture. + +✅ Pros: +Avoids expensive refactors and new dependencies. +- Fully customized to our existing codebase. +- Keeps Django templates intact while allowing dynamic updates. +- No need for additional build tools or frontend frameworks. + +❌ Cons: +- Requires designing our own structured approach to AJAX. +- Testing and maintainability must be carefully considered. + +This approach aligns with our existing architecture and skill set while meeting stakeholder demands for interactivity. + +## Decision +We chose Option 4: Extending our use of vanilla JavaScript with AJAX. + +## Consequences +1. Ownership of Solution + - We fully control the implementation without external dependencies. + +2. Maintainability + - Our AJAX implementation will follow an object-oriented approach, with a base class for components requiring pagination, search, and filtering. + +3. Backend Considerations + - Views handling AJAX responses will be explicitly designated as JSON views. + +4. Scalability + - While this approach works now, we may need to reassess in the future if interactivity demands continue to grow. + +This decision allows us to enhance the application's responsiveness without disrupting existing architecture or delaying feature development. From e872a0ea3ad6a9231b57ccaef96ae20bd2e18bac Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Wed, 19 Feb 2025 17:26:36 -0500 Subject: [PATCH 10/14] cleanup --- docs/architecture/decisions/0027-ajax-for-dynamic-content.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture/decisions/0027-ajax-for-dynamic-content.md b/docs/architecture/decisions/0027-ajax-for-dynamic-content.md index 6e22a67e7..b1d92fc32 100644 --- a/docs/architecture/decisions/0027-ajax-for-dynamic-content.md +++ b/docs/architecture/decisions/0027-ajax-for-dynamic-content.md @@ -47,7 +47,7 @@ Instead of a full migration, this approach involves integrating a modern JavaScr This approach would still introduce diverging implementation stacks, leading to long-term maintenance challenges. -**Option 3:**Use a Lightweight JavaScript Framework (e.g., HTMX, HTMZ) +**Option 3:** Use a Lightweight JavaScript Framework (e.g., HTMX, HTMZ) Instead of React or Vue, this approach involves using a minimal JavaScript framework like HTMX or HTMZ to enhance interactivity while preserving Django’s server-rendered structure. ✅ Pros: From 74f82b5e8667cd4e971d173b4a8542550bef0255 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Wed, 19 Feb 2025 17:27:57 -0500 Subject: [PATCH 11/14] cleanup --- .../architecture/decisions/0027-ajax-for-dynamic-content.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/architecture/decisions/0027-ajax-for-dynamic-content.md b/docs/architecture/decisions/0027-ajax-for-dynamic-content.md index b1d92fc32..6c4b750b1 100644 --- a/docs/architecture/decisions/0027-ajax-for-dynamic-content.md +++ b/docs/architecture/decisions/0027-ajax-for-dynamic-content.md @@ -33,6 +33,8 @@ This approach involves refactoring Django into a backend-only service and adopti This approach was deemed too costly in terms of both time and resources. +--- + **Option 2:** Adopt a Modern JS Framework for Select Parts of the Application Instead of a full migration, this approach involves integrating a modern JavaScript framework (e.g., React or Vue) only in areas that require high interactivity. @@ -47,6 +49,8 @@ Instead of a full migration, this approach involves integrating a modern JavaScr This approach would still introduce diverging implementation stacks, leading to long-term maintenance challenges. +--- + **Option 3:** Use a Lightweight JavaScript Framework (e.g., HTMX, HTMZ) Instead of React or Vue, this approach involves using a minimal JavaScript framework like HTMX or HTMZ to enhance interactivity while preserving Django’s server-rendered structure. @@ -62,6 +66,8 @@ Instead of React or Vue, this approach involves using a minimal JavaScript frame Ultimately, we determined that the benefits did not outweigh the potential downsides. +--- + **Option 4:** Extend Vanilla JavaScript with AJAX (Selected Option) This approach involves incrementally enhancing Django’s server-rendered pages with AJAX while maintaining our existing architecture. From 234a846d5a3f4980c57b364407250181c1dd4e33 Mon Sep 17 00:00:00 2001 From: Jaxon Silva Date: Wed, 19 Feb 2025 16:31:26 -0800 Subject: [PATCH 12/14] Update status_change_rejected.txt Added additional state for "organization not eligible" --- src/registrar/templates/emails/status_change_rejected.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/templates/emails/status_change_rejected.txt b/src/registrar/templates/emails/status_change_rejected.txt index a222e6088..e865031fa 100644 --- a/src/registrar/templates/emails/status_change_rejected.txt +++ b/src/registrar/templates/emails/status_change_rejected.txt @@ -68,7 +68,7 @@ Learn more about: NEED ASSISTANCE? If you have questions about this domain request or need help choosing a new domain name, reply to this email. {% endif %} -{% if reason != domain_request.RejectionReasons.REQUESTOR_NOT_ELIGIBLE %} +{% if reason != domain_request.RejectionReasons.REQUESTOR_NOT_ELIGIBLE and reason != domain_request.RejectionReasons.ORG_NOT_ELIGIBLE %} THANK YOU .Gov helps the public identify official, trusted information. Thank you for requesting a .gov domain. From eb02869df82bf363c70aadc63dcb564376fd5ddd Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 20 Feb 2025 08:57:09 -0700 Subject: [PATCH 13/14] lint --- src/registrar/assets/src/js/getgov-admin/button-utils.js | 1 - src/registrar/templatetags/custom_filters.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/registrar/assets/src/js/getgov-admin/button-utils.js b/src/registrar/assets/src/js/getgov-admin/button-utils.js index 79d9091d6..e3746d289 100644 --- a/src/registrar/assets/src/js/getgov-admin/button-utils.js +++ b/src/registrar/assets/src/js/getgov-admin/button-utils.js @@ -7,7 +7,6 @@ export function initButtonLinks() { button.addEventListener('click', function() { // Equivalent to button.getAttribute("data-href") const href = this.dataset.href; - console.log(`in loop: ${href}`) if (href) { window.location.href = href; } diff --git a/src/registrar/templatetags/custom_filters.py b/src/registrar/templatetags/custom_filters.py index cf2abf447..e02a29e73 100644 --- a/src/registrar/templatetags/custom_filters.py +++ b/src/registrar/templatetags/custom_filters.py @@ -32,7 +32,7 @@ def extract_a_text(value): text_only = re.sub(text_pattern, "", content) # Clean up any extra whitespace return text_only.strip() - + return "" From 9e9e0f438e27b77c7ae463bfe923f2f24204cb84 Mon Sep 17 00:00:00 2001 From: Alysia Broddrick Date: Thu, 27 Feb 2025 13:56:07 -0800 Subject: [PATCH 14/14] added bug label --- .github/ISSUE_TEMPLATE/bug.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 559be3fca..f29dacc43 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -1,6 +1,6 @@ name: Bug description: Report a bug or problem with the application -labels: ["bug"] +labels: ["bug","dev"] body: - type: markdown