From 1b255366afd42dd4b35a132695096f93a6846dfa Mon Sep 17 00:00:00 2001 From: CocoByte Date: Mon, 17 Feb 2025 14:31:40 -0700 Subject: [PATCH 01/15] First draft --- .../js/getgov-admin/domain-request-form.js | 41 ++++++++++++++++++- .../src/js/getgov-admin/helpers-admin.js | 20 +++++++++ .../assets/src/js/getgov-admin/main.js | 4 +- .../templates/admin/change_list.html | 15 +++++++ 4 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/registrar/assets/src/js/getgov-admin/domain-request-form.js b/src/registrar/assets/src/js/getgov-admin/domain-request-form.js index b3d14839e..9352dd6b6 100644 --- a/src/registrar/assets/src/js/getgov-admin/domain-request-form.js +++ b/src/registrar/assets/src/js/getgov-admin/domain-request-form.js @@ -1,4 +1,4 @@ -import { hideElement, showElement, addOrRemoveSessionBoolean } from './helpers-admin.js'; +import { hideElement, showElement, addOrRemoveSessionBoolea, announceForScreenReaders } from './helpers-admin.js'; import { handlePortfolioSelection } from './helpers-portfolio-dynamic-fields.js'; function displayModalOnDropdownClick(linkClickedDisplaysModal, statusDropdown, actionButton, valueToCheck){ @@ -684,3 +684,42 @@ export function initDynamicDomainRequestFields(){ handleSuborgFieldsAndButtons(); } } + +export function initFilterFocusListeners() { + document.addEventListener("DOMContentLoaded", function() { + let filters = document.querySelectorAll("#changelist-filter li a"); // Get list of all filter links + let clickedFilter = false; // Used to determine if we are truly navigating away or not + + // Restore focus from localStorage if it exists + let lastClickedFilter = localStorage.getItem("admin_filter_focus"); + if (lastClickedFilter) { + let focusedElement = document.querySelector(`#changelist-filter li a[href='${lastClickedFilter}']`); + if (focusedElement) { + // Focus the element + focusedElement.setAttribute("tabindex", "-1"); + focusedElement.focus({ preventScroll: true }); + + // Announce focus change for screen readers + announceForScreenReaders("Filter refocused on " + focusedElement.textContent); + } + } + + // Setup listeners. When a filter is clicked, save it as the filter to focus after page refresh + filters.forEach(filter => { + filter.addEventListener("click", function() { + // Save this filter in local storage so we can focus it after page refresh + localStorage.setItem("admin_filter_focus", this.getAttribute("href")); + + // Mark that a filter was clicked so that our beforeunload listener doesn't clear the local storage + clickedFilter = true; + }); + }); + + // Clear focus selection in local storage if user is truly leaving the page + window.addEventListener("beforeunload", function(event) { + if (!clickedFilter) { + localStorage.removeItem("admin_filter_focus"); + } + }); + }); +} \ No newline at end of file diff --git a/src/registrar/assets/src/js/getgov-admin/helpers-admin.js b/src/registrar/assets/src/js/getgov-admin/helpers-admin.js index ff618a67d..de264f34f 100644 --- a/src/registrar/assets/src/js/getgov-admin/helpers-admin.js +++ b/src/registrar/assets/src/js/getgov-admin/helpers-admin.js @@ -22,3 +22,23 @@ export function addOrRemoveSessionBoolean(name, add){ sessionStorage.removeItem(name); } } + +/** + * Creates a temporary live region to announce messages for screen readers. + */ +export function announceForScreenReaders(message) { + let liveRegion = document.createElement("div"); + liveRegion.setAttribute("aria-live", "assertive"); + liveRegion.setAttribute("role", "alert"); + liveRegion.style.position = "absolute"; + liveRegion.style.left = "-9999px"; + document.body.appendChild(liveRegion); + + // Delay the update slightly to ensure it's recognized + setTimeout(() => { + liveRegion.textContent = message; + setTimeout(() => { + document.body.removeChild(liveRegion); + }, 1000); + }, 100); +} \ No newline at end of file diff --git a/src/registrar/assets/src/js/getgov-admin/main.js b/src/registrar/assets/src/js/getgov-admin/main.js index 64be572b2..22266a2c8 100644 --- a/src/registrar/assets/src/js/getgov-admin/main.js +++ b/src/registrar/assets/src/js/getgov-admin/main.js @@ -10,7 +10,8 @@ import { initRejectedEmail, initApprovedDomain, initCopyRequestSummary, - initDynamicDomainRequestFields } from './domain-request-form.js'; + initDynamicDomainRequestFields, + initFilterFocusListeners } from './domain-request-form.js'; import { initDomainFormTargetBlankButtons } from './domain-form.js'; import { initDynamicPortfolioFields } from './portfolio-form.js'; import { initDynamicDomainInformationFields } from './domain-information-form.js'; @@ -31,6 +32,7 @@ initRejectedEmail(); initApprovedDomain(); initCopyRequestSummary(); initDynamicDomainRequestFields(); +initFilterFocusListeners(); // Domain initDomainFormTargetBlankButtons(); diff --git a/src/registrar/templates/admin/change_list.html b/src/registrar/templates/admin/change_list.html index 43abf0861..806067025 100644 --- a/src/registrar/templates/admin/change_list.html +++ b/src/registrar/templates/admin/change_list.html @@ -39,6 +39,21 @@ {% endblock %} +{% for spec in cl.filter_specs %} +
+

{{ spec.title }}

+ +
+{% endfor %} + {% comment %} Replace the Django ul markup with a div. We'll replace the li with a p in change_list_object_tools {% endcomment %} {% block object-tools %}
From dac0841a0105cb8945f551c9fd13fc115e50f45e Mon Sep 17 00:00:00 2001 From: CocoByte Date: Mon, 17 Feb 2025 15:21:58 -0700 Subject: [PATCH 02/15] Add filter IDs and target those for focus --- .../js/getgov-admin/domain-request-form.js | 29 +++++++++---------- src/registrar/templates/admin/filter.html | 12 ++++++++ .../admin/multiple_choice_list_filter.html | 6 ++-- 3 files changed, 29 insertions(+), 18 deletions(-) create mode 100644 src/registrar/templates/admin/filter.html diff --git a/src/registrar/assets/src/js/getgov-admin/domain-request-form.js b/src/registrar/assets/src/js/getgov-admin/domain-request-form.js index 9352dd6b6..50fcbf0b1 100644 --- a/src/registrar/assets/src/js/getgov-admin/domain-request-form.js +++ b/src/registrar/assets/src/js/getgov-admin/domain-request-form.js @@ -690,35 +690,34 @@ export function initFilterFocusListeners() { let filters = document.querySelectorAll("#changelist-filter li a"); // Get list of all filter links let clickedFilter = false; // Used to determine if we are truly navigating away or not - // Restore focus from localStorage if it exists - let lastClickedFilter = localStorage.getItem("admin_filter_focus"); - if (lastClickedFilter) { - let focusedElement = document.querySelector(`#changelist-filter li a[href='${lastClickedFilter}']`); + // Restore focus from localStorage + let lastClickedFilterId = localStorage.getItem("admin_filter_focus_id"); + if (lastClickedFilterId) { + let focusedElement = document.getElementById(lastClickedFilterId); if (focusedElement) { - // Focus the element + //Focus the element focusedElement.setAttribute("tabindex", "-1"); focusedElement.focus({ preventScroll: true }); - + // Announce focus change for screen readers announceForScreenReaders("Filter refocused on " + focusedElement.textContent); } } - - // Setup listeners. When a filter is clicked, save it as the filter to focus after page refresh + + // Capture clicked filter and store its ID filters.forEach(filter => { filter.addEventListener("click", function() { - // Save this filter in local storage so we can focus it after page refresh - localStorage.setItem("admin_filter_focus", this.getAttribute("href")); - - // Mark that a filter was clicked so that our beforeunload listener doesn't clear the local storage - clickedFilter = true; + localStorage.setItem("admin_filter_focus_id", this.id); + clickedFilter = true; // Mark that a filter was clicked }); }); - + // Clear focus selection in local storage if user is truly leaving the page window.addEventListener("beforeunload", function(event) { if (!clickedFilter) { - localStorage.removeItem("admin_filter_focus"); + // If the user did not click a filter and the page is refreshing, + // clear the filter focus id from local storage + localStorage.removeItem("admin_filter_focus_id"); } }); }); diff --git a/src/registrar/templates/admin/filter.html b/src/registrar/templates/admin/filter.html new file mode 100644 index 000000000..035d4ad30 --- /dev/null +++ b/src/registrar/templates/admin/filter.html @@ -0,0 +1,12 @@ +{% load i18n %} +
+ + {% blocktranslate with filter_title=title %} By {{ filter_title }} {% endblocktranslate %} + + +
\ No newline at end of file diff --git a/src/registrar/templates/django/admin/multiple_choice_list_filter.html b/src/registrar/templates/django/admin/multiple_choice_list_filter.html index 167059594..7ae2f2bad 100644 --- a/src/registrar/templates/django/admin/multiple_choice_list_filter.html +++ b/src/registrar/templates/django/admin/multiple_choice_list_filter.html @@ -7,7 +7,7 @@ {% for choice in choices %} {% if choice.reset %} - {{ choice.display }} + {{ choice.display }} {% endif %} {% endfor %} @@ -15,7 +15,7 @@ {% if not choice.reset %} {% if choice.selected and choice.exclude_query_string %} - {{ choice.display }} + {{ choice.display }} @@ -25,7 +25,7 @@ {% endif %} {% if not choice.selected and choice.include_query_string %} - {{ choice.display }} + {{ choice.display }} From 98f4fe773269d37bd00bf7438dfc2472fa03e31f Mon Sep 17 00:00:00 2001 From: CocoByte Date: Mon, 17 Feb 2025 15:35:47 -0700 Subject: [PATCH 03/15] fix typo --- src/registrar/assets/src/js/getgov-admin/domain-request-form.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/assets/src/js/getgov-admin/domain-request-form.js b/src/registrar/assets/src/js/getgov-admin/domain-request-form.js index 50fcbf0b1..34c6a42f3 100644 --- a/src/registrar/assets/src/js/getgov-admin/domain-request-form.js +++ b/src/registrar/assets/src/js/getgov-admin/domain-request-form.js @@ -1,4 +1,4 @@ -import { hideElement, showElement, addOrRemoveSessionBoolea, announceForScreenReaders } from './helpers-admin.js'; +import { hideElement, showElement, addOrRemoveSessionBoolean, announceForScreenReaders } from './helpers-admin.js'; import { handlePortfolioSelection } from './helpers-portfolio-dynamic-fields.js'; function displayModalOnDropdownClick(linkClickedDisplaysModal, statusDropdown, actionButton, valueToCheck){ From bbcabbe1b06cb528936c64e7d98cbf558c567677 Mon Sep 17 00:00:00 2001 From: CocoByte Date: Mon, 17 Feb 2025 15:37:17 -0700 Subject: [PATCH 04/15] remove experiment --- src/registrar/templates/admin/change_list.html | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/registrar/templates/admin/change_list.html b/src/registrar/templates/admin/change_list.html index 806067025..43abf0861 100644 --- a/src/registrar/templates/admin/change_list.html +++ b/src/registrar/templates/admin/change_list.html @@ -39,21 +39,6 @@ {% endblock %} -{% for spec in cl.filter_specs %} - -{% endfor %} - {% comment %} Replace the Django ul markup with a div. We'll replace the li with a p in change_list_object_tools {% endcomment %} {% block object-tools %}
From 0a0b9c04906253f0a494682963c9bade5448d68c Mon Sep 17 00:00:00 2001 From: CocoByte Date: Wed, 19 Feb 2025 12:50:12 -0800 Subject: [PATCH 05/15] Fixed multi-list bug --- .../templates/django/admin/multiple_choice_list_filter.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/registrar/templates/django/admin/multiple_choice_list_filter.html b/src/registrar/templates/django/admin/multiple_choice_list_filter.html index 7ae2f2bad..4fb70565e 100644 --- a/src/registrar/templates/django/admin/multiple_choice_list_filter.html +++ b/src/registrar/templates/django/admin/multiple_choice_list_filter.html @@ -13,7 +13,7 @@ {% endfor %} {% for choice in choices %} {% if not choice.reset %} - + {% if choice.selected and choice.exclude_query_string %} {{ choice.display }} - {% endif %} - {% if not choice.selected and choice.include_query_string %} + {% elif not choice.selected and choice.include_query_string %} {{ choice.display }}

Manage your domains

- Start a new domain request - +

{% include "includes/domains_table.html" with user_domain_count=user_domain_count %} diff --git a/src/registrar/templates/portfolio_requests.html b/src/registrar/templates/portfolio_requests.html index 58fbde10c..4f366d0c6 100644 --- a/src/registrar/templates/portfolio_requests.html +++ b/src/registrar/templates/portfolio_requests.html @@ -26,10 +26,10 @@ {% else %} From e506604e7b2e24f3d0d87d745f6b0bcfd8ac7005 Mon Sep 17 00:00:00 2001 From: CuriousX Date: Mon, 3 Mar 2025 11:41:32 -0700 Subject: [PATCH 08/15] Update src/registrar/templates/admin/filter.html Co-authored-by: zandercymatics <141044360+zandercymatics@users.noreply.github.com> --- src/registrar/templates/admin/filter.html | 1 + 1 file changed, 1 insertion(+) diff --git a/src/registrar/templates/admin/filter.html b/src/registrar/templates/admin/filter.html index 035d4ad30..abe3ad282 100644 --- a/src/registrar/templates/admin/filter.html +++ b/src/registrar/templates/admin/filter.html @@ -1,3 +1,4 @@ +{% comment %} Override of this file: https://github.com/django/django/blob/main/django/contrib/admin/templates/admin/filter.html {% endcomment %} {% load i18n %}
From ea0e9e2f7eebc8183ef9f820cb219b9e751ea840 Mon Sep 17 00:00:00 2001 From: CocoByte Date: Mon, 3 Mar 2025 11:41:49 -0700 Subject: [PATCH 09/15] cleanup --- .../assets/src/js/getgov-admin/domain-request-form.js | 10 +--------- .../assets/src/js/getgov-admin/helpers-admin.js | 3 +-- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/registrar/assets/src/js/getgov-admin/domain-request-form.js b/src/registrar/assets/src/js/getgov-admin/domain-request-form.js index 34c6a42f3..1eec1b70e 100644 --- a/src/registrar/assets/src/js/getgov-admin/domain-request-form.js +++ b/src/registrar/assets/src/js/getgov-admin/domain-request-form.js @@ -701,6 +701,7 @@ export function initFilterFocusListeners() { // Announce focus change for screen readers announceForScreenReaders("Filter refocused on " + focusedElement.textContent); + localStorage.removeItem("admin_filter_focus_id"); } } @@ -711,14 +712,5 @@ export function initFilterFocusListeners() { clickedFilter = true; // Mark that a filter was clicked }); }); - - // Clear focus selection in local storage if user is truly leaving the page - window.addEventListener("beforeunload", function(event) { - if (!clickedFilter) { - // If the user did not click a filter and the page is refreshing, - // clear the filter focus id from local storage - localStorage.removeItem("admin_filter_focus_id"); - } - }); }); } \ No newline at end of file diff --git a/src/registrar/assets/src/js/getgov-admin/helpers-admin.js b/src/registrar/assets/src/js/getgov-admin/helpers-admin.js index 227355504..5ec78f6b0 100644 --- a/src/registrar/assets/src/js/getgov-admin/helpers-admin.js +++ b/src/registrar/assets/src/js/getgov-admin/helpers-admin.js @@ -40,8 +40,7 @@ export function announceForScreenReaders(message) { let liveRegion = document.createElement("div"); liveRegion.setAttribute("aria-live", "assertive"); liveRegion.setAttribute("role", "alert"); - liveRegion.style.position = "absolute"; - liveRegion.style.left = "-9999px"; + liveRegion.setAttribute("class", "usa-sr-only"); document.body.appendChild(liveRegion); // Delay the update slightly to ensure it's recognized From f937360dba89eef8c0c8686ec608d0d631790db5 Mon Sep 17 00:00:00 2001 From: CuriousX Date: Mon, 3 Mar 2025 15:21:34 -0700 Subject: [PATCH 10/15] Update src/registrar/assets/src/js/getgov-admin/domain-request-form.js Co-authored-by: zandercymatics <141044360+zandercymatics@users.noreply.github.com> --- src/registrar/assets/src/js/getgov-admin/domain-request-form.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/assets/src/js/getgov-admin/domain-request-form.js b/src/registrar/assets/src/js/getgov-admin/domain-request-form.js index 1eec1b70e..db6467875 100644 --- a/src/registrar/assets/src/js/getgov-admin/domain-request-form.js +++ b/src/registrar/assets/src/js/getgov-admin/domain-request-form.js @@ -696,7 +696,7 @@ export function initFilterFocusListeners() { let focusedElement = document.getElementById(lastClickedFilterId); if (focusedElement) { //Focus the element - focusedElement.setAttribute("tabindex", "-1"); + focusedElement.setAttribute("tabindex", "0"); focusedElement.focus({ preventScroll: true }); // Announce focus change for screen readers From bc914c4ea9931f8b05fcae3f9451d796ee02521f Mon Sep 17 00:00:00 2001 From: CocoByte Date: Mon, 3 Mar 2025 15:59:18 -0700 Subject: [PATCH 11/15] fix (removed stray code - possible merge error or slip of a keystroke) --- .../templates/django/admin/multiple_choice_list_filter.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/registrar/templates/django/admin/multiple_choice_list_filter.html b/src/registrar/templates/django/admin/multiple_choice_list_filter.html index a69d37c72..1a0c78ffc 100644 --- a/src/registrar/templates/django/admin/multiple_choice_list_filter.html +++ b/src/registrar/templates/django/admin/multiple_choice_list_filter.html @@ -37,5 +37,4 @@ {% endif %} {% endfor %} -
- {{ choice.display }} + \ No newline at end of file From e8f2730f3221a69424b343e4655257bc0821aa6f Mon Sep 17 00:00:00 2001 From: CuriousX Date: Tue, 4 Mar 2025 10:44:42 -0700 Subject: [PATCH 12/15] Update src/registrar/templates/django/admin/multiple_choice_list_filter.html Co-authored-by: zandercymatics <141044360+zandercymatics@users.noreply.github.com> --- .../templates/django/admin/multiple_choice_list_filter.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/templates/django/admin/multiple_choice_list_filter.html b/src/registrar/templates/django/admin/multiple_choice_list_filter.html index 1a0c78ffc..b149a6d56 100644 --- a/src/registrar/templates/django/admin/multiple_choice_list_filter.html +++ b/src/registrar/templates/django/admin/multiple_choice_list_filter.html @@ -16,7 +16,7 @@ {% for choice in choices %} {% if not choice.reset %} - + {% if choice.selected and choice.exclude_query_string %} {{ choice.display }}