diff --git a/src/registrar/assets/js/uswds-edited.js b/src/registrar/assets/js/uswds-edited.js
index ae246b05c..d8664d5bf 100644
--- a/src/registrar/assets/js/uswds-edited.js
+++ b/src/registrar/assets/js/uswds-edited.js
@@ -5284,7 +5284,10 @@ const setUpModal = baseComponent => {
overlayDiv.classList.add(OVERLAY_CLASSNAME);
// Set attributes
- modalWrapper.setAttribute("role", "dialog");
+ // DOTGOV
+ // Removes the dialog role as this causes a double readout bug with screenreaders
+ // modalWrapper.setAttribute("role", "dialog");
+ // END DOTGOV
modalWrapper.setAttribute("id", modalID);
if (ariaLabelledBy) {
modalWrapper.setAttribute("aria-labelledby", ariaLabelledBy);
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..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
@@ -1,4 +1,4 @@
-import { hideElement, showElement, addOrRemoveSessionBoolean } 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){
@@ -684,3 +684,33 @@ 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
+ let lastClickedFilterId = localStorage.getItem("admin_filter_focus_id");
+ if (lastClickedFilterId) {
+ let focusedElement = document.getElementById(lastClickedFilterId);
+ if (focusedElement) {
+ //Focus the element
+ focusedElement.setAttribute("tabindex", "0");
+ focusedElement.focus({ preventScroll: true });
+
+ // Announce focus change for screen readers
+ announceForScreenReaders("Filter refocused on " + focusedElement.textContent);
+ localStorage.removeItem("admin_filter_focus_id");
+ }
+ }
+
+ // Capture clicked filter and store its ID
+ filters.forEach(filter => {
+ filter.addEventListener("click", function() {
+ localStorage.setItem("admin_filter_focus_id", this.id);
+ clickedFilter = true; // Mark that a filter was clicked
+ });
+ });
+ });
+}
\ 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 8055e29d3..5ec78f6b0 100644
--- a/src/registrar/assets/src/js/getgov-admin/helpers-admin.js
+++ b/src/registrar/assets/src/js/getgov-admin/helpers-admin.js
@@ -32,3 +32,22 @@ export function getParameterByName(name, url) {
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
+
+/**
+ * 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.setAttribute("class", "usa-sr-only");
+ 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 7eb1fc8cd..112740dd9 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';
@@ -34,6 +35,7 @@ initRejectedEmail();
initApprovedDomain();
initCopyRequestSummary();
initDynamicDomainRequestFields();
+initFilterFocusListeners();
// Domain
initDomainFormTargetBlankButtons();
diff --git a/src/registrar/assets/src/js/getgov/main.js b/src/registrar/assets/src/js/getgov/main.js
index 637488187..0529d3614 100644
--- a/src/registrar/assets/src/js/getgov/main.js
+++ b/src/registrar/assets/src/js/getgov/main.js
@@ -16,6 +16,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 { initButtonLinks } from '../getgov-admin/button-utils.js';
initDomainValidators();
@@ -50,3 +51,5 @@ initFormErrorHandling();
initPortfolioMemberPageRadio();
initPortfolioNewMemberPageToggle();
initAddNewMemberPageListeners();
+
+initButtonLinks();
diff --git a/src/registrar/templates/admin/filter.html b/src/registrar/templates/admin/filter.html
new file mode 100644
index 000000000..abe3ad282
--- /dev/null
+++ b/src/registrar/templates/admin/filter.html
@@ -0,0 +1,13 @@
+{% comment %} Override of this file: https://github.com/django/django/blob/main/django/contrib/admin/templates/admin/filter.html {% endcomment %}
+{% load i18n %}
+
+ {% blocktranslate with filter_title=title %} By {{ filter_title }} {% endblocktranslate %}
+
+
+ {% for choice in choices %}
+
+